I'm trying to write a character device driver in linux. Unfortunately it's not working for any numbers greater than 255.
I want this driver specifically to work with value of type long. Anytime I input a value greater than 255, the numbers wrong. 256 goes to 0 etc.
I've written a simple character device driver that shows the problem, there might be a lot of unused include statements as I copied my full driver and deleted almost everything:
chartest.c
#include <linux/init.h>
#include <linux/module.h> /* I mean this is a module after all! */
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h> /* For current task information */
#include <linux/fs.h> /* For file operations */
#include <linux/types.h> /* dev_t: device number data type */
#include <linux/cdev.h> /* cdev is the module data type that the kernel sees */
#include <asm/uaccess.h> /* For routines to copy data to/from user space */
#include <linux/uaccess.h>
#include <linux/slab.h> /* kmalloc/kfree */
MODULE_LICENSE("GPL");
#define DRIVER_NAME "chartest"
#define MAJOR_NUM 230
#define MINOR_NUM 0
struct cdev *cdev;
int test_device_open(struct inode *inode, struct file *fp) {
return 0;
}
int test_device_release(struct inode *inode, struct file *fp) {
return 0;
}
ssize_t test_read(struct file *fp, char __user *buffer, size_t count, loff_t *f_pos) {
return count;
}
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
userInput = kmalloc(count, GFP_KERNEL);
get_user(*userInput, buffer);
printk(KERN_NOTICE "Value before cast: %ld\n", (long) *userInput);
userOperand = (long) *userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
/*
* Declaration of function for open file operations
*/
static struct file_operations test_fops = {
.owner = THIS_MODULE,
.read = test_read,
.write = test_write,
.open = test_device_open,
.release = test_device_release,
};
// Initialization function
static int __init test_init(void)
{
// Register device number:
int err = 0;
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
err = register_chrdev_region(device_number, 1, DRIVER_NAME);
if (err < 0) {
printk(KERN_ALERT "Could not allocate device number.\n");
return err;
}
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &test_fops;
err = cdev_add(cdev, device_number, 1);
if (err) {
printk("Error allocating cdev.\n");
}
printk(KERN_ALERT "Test Initialized. Major Number: %d\n", MAJOR_NUM);
return 0;
}
// Exit function:
static void __exit test_exit(void)
{
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
// Remove char device */
cdev_del(cdev);
/* Unregister Device Number: */
unregister_chrdev_region(device_number, 1);
printk(KERN_ALERT "TestDriver %d destroyed.\n", MAJOR_NUM);
}
module_init(test_init);
module_exit(test_exit);
Small test program:
maintest.c:
#include <unistd.h>
#include <fcntl.h>
int main(void) {
long input = 256;
int fd = open("/dev/chartest0", O_RDWR);
write(fd, &input, sizeof(long));
close(fd);
return 0;
}
The printk statements gives the following output with the given input of 256:
Write Eunction Entered.
Write count: 8, Write fp: 0
Value before cast: 0
Value after cast: 0
This also fails with copy_from_user given an in put size of 8 bytes. It also fails when iterating through the buffer one byte at a time and copying the data. I've tried everything.
If you are graciously willing to help, compile with:
MakeFile
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := chartest.o
endif
then in the same directory:
sudo insmod chartest.ko
finally:
sudo mknod -m 777 /dev/chartest0 c 230 0
Then you can compile maindriver.c and run it to test.
Can someone please help me fix this issue?
you can not use get_user the way you do:
from get_user doc
This macro copies a single simple variable from user space to kernel space. It supports simple types like char and int, but not larger data types like structures or arrays.ptr must have pointer-to-simple-variable type, and the result of dereferencing ptr must be assignable to x without a cast.
With get_user, you will only copy the first character.
You need to use copy_from_user, this function can copy array and structure, not only simple types:
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
userInput = kmalloc(count, GFP_KERNEL);
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userInput */
copy_from_user(userInput, buffer, count);
userOperand = *(long*)userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
You can also copy from char * to long in copy_from_user (no memory alloc in that case):
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userOperand */
copy_from_user(&userOperand, buffer, sizeof userOperand);
printk(KERN_NOTICE "Value after reading: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
return count;
}
You use macro get_user incorrectly.
Its first argument should be literally a variable name, not the address of it.
Its second argument should be typed pointer to the user space data. And exactly type of that pointer is used for determine the size of the data to read.
Correct:
long userOperand;
...
get_user(userOperand, (const long __user*)buffer);
printk(KERN_NOTICE "Value written: %ld\n", userOperand);
Note to the case of the second argument: it is needed for read long instead of char (because buffer parameter is a pointer to the char).
Note, that code above doesn't use your userInput variable, so you don't need to define it and don't need to allocate memory.
Note, that .write method could be called with any count parameter which denotes number of bytes passed from the user. Since get_user always tries to read a predefined number of bytes (in your case this number is equal to the size of the long), it would be a user-friendly to check that count is equal to that number (or, at least, is not less than given number):
if (count != sizeof(long)) {
return -EINVAL;
}
Related
I try to get the target of symlink dentries in a kernel module, i.e. the dentry on which my dentry points.
I use this approach:
int print_dentry(struct dentry *d) {
struct path p;
char *buffer, *path_name;
int ret;
buffer = (char *)__get_free_page(GFP_KERNEL);
if (!buffer)
return -ENOMEM;
path_name = dentry_path_raw(d, buffer, PAGE_SIZE);
if (IS_ERR(path_name))
printk(KERN_ERR "ERR");
if ((ret=kern_path(path_name, LOOKUP_FOLLOW, &p))) {
printk("kern_path returned %d for path_name \"%s\", inode %ld\n", ret, path_name, d->d_inode->i_ino);
return 0;
}
printk_once(KERN_INFO "Path %s -> dentry_uid %ld\n", path_name, p.dentry->d_inode->i_ino);
free_page((unsigned long)buffer);
return 0;
}
However, dentry_path_raw doesn't return the absolute path, but a path relative to the vfsmount.
Hence I get errors like this when there is a vfsmount e.g.
kern_path returned -2 for path_name "/self", inode 4026531841
Which corresponds to /proc/self
ls -ial /proc/self
4026531841 lrwxrwxrwx 1 root root 0 5 déc. 04:28 /proc/self -> 1341
Is there a way to get the absolute path so that I can give it to kern_path()? Or maybe another approach to follow the symlink and get the associated dentry?
I don't think I can use directly d_absolute_path or prepend_path since they take as input a struct path* or a char* and I only have a access to a struct dentry* or a char*
Looking at the source code here, it seems that when you call dentry_path_raw() it actually calls another function that does the following:
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Write full pathname from the root of the filesystem into the buffer.
*/
static char *__dentry_path(const struct dentry *d, struct prepend_buffer *p)
{
const struct dentry *dentry;
struct prepend_buffer b;
int seq = 0;
rcu_read_lock();
restart:
dentry = d;
b = *p;
read_seqbegin_or_lock(&rename_lock, &seq);
while (!IS_ROOT(dentry)) {
const struct dentry *parent = dentry->d_parent;
prefetch(parent);
if (!prepend_name(&b, &dentry->d_name))
break;
dentry = parent;
}
if (!(seq & 1))
rcu_read_unlock();
if (need_seqretry(&rename_lock, seq)) {
seq = 1;
goto restart;
}
done_seqretry(&rename_lock, seq);
if (b.len == p->len)
prepend_char(&b, '/');
return extract_string(&b);
}
And everything seems to be in the description of this function. It writes the full path into the buffer, not in its return value, which share it's return with dentry_path_raw().
So, you should use the value pushed into your buffer instead of file_name, which is just the name returned by the function dentry_path_raw().
Note: dentry_path_raw() doesn't resolve symlinks, if you really want to resolve them you should probably use dentry_path() instead.
All these functions are available since linux v2.6.26/v2.6.38
I am trying to read/Write a variable in the linux kernel module using /proc file entry facility.
Kernel module compiles successfully but when tried writing it via
echo 1 > My_file
This operation didn't finish.
Also, dmesg console is continuously flooded with some random value.
[ 1171.481231] proc_write_flag New_Flag 1124646486
[ 1171.481245] proc_write_flag New_Flag 1124646486
[ 1171.481259] proc_write_flag New_Flag 1124646486
[ 1171.481271] proc_write_flag New_Flag 1124646486
[ 1171.481473] ^C
I am new to linux device drivers and trying to use /proc facility provided by the linux kernel. I tried removing the this kernel module, but again, the operation didn't finish.
what is causing this behaviour and how can i rectify it?
Here is the code:
int my_flag;
static struct proc_dir_entry *pdir = NULL;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GPL");
static ssize_t proc_read_flag(struct file* page,char __user * data, size_t count, loff_t *offset);
static ssize_t proc_write_flag(struct file *file, const char __user* ubuf, size_t count, loff_t* offset);
static struct file_operations myops =
{
.owner = THIS_MODULE,
.read = proc_read_flag,
.write= proc_write_flag,
};
//ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//Updated Read function after the reply.
static ssize_t proc_read_flag(struct file* page,char __user * data, size_t count,loff_t *offset)
{
int ret;
if( count >my_flag) //my_flag holds the count of chars received by write function.
count = my_flag;
ret = copy_to_user(data, my_buf, my_flag );
printk("%s: ret = %d ,my_flag %d\n",__FUNCTION__, ret, my_flag);
return ( my_flag - ret );
}
//ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//Updated Write function After the reply.
static ssize_t proc_write_flag(struct file *file, const char __user* ubuf, size_t count, loff_t* offset)
{
if( copy_from_user(my_buf,ubuf,count) ){ //Returns No. of bytes could not copy
return -EFAULT;
}
my_flag = count;
printk("%s New_Flag %d Data: %s\n",__FUNCTION__,my_flag,my_buf);
return count;
}
int init_module(void)
{
struct proc_dir_entry *pfile = NULL;
pdir = proc_mkdir("My_dir",NULL);
if(!pdir){
return -ENOMEM;
}
pfile = proc_create("My_file", 0666, pdir, &myops);
if(!pfile)
return -ENOMEM;
printk("Proc_entry Created Successfully, Module initialized\n");
return 0;
}
void cleanup_function(void)
{
remove_proc_entry("My_file", pdir);
remove_proc_entry("My_dir", NULL);
printk("Removing Proc_entry!!!");
}
The write function should return the number of bytes you processed.
In your case, 'proc_write_flag' function is returning 'ret' which is 0.
which means it will be invoked repeatedly until you process 'count' number of bytes from 'ubuf'
Similarly, 'proc_read_flag' should return the number of bytes you wrote into 'data'. In your case it is returning 0 (len)
There are other problems in 'proc_write_flag' function.
'buf' array isn't initialize and casting 'buf'(address) to int will not give you the expected answer.
Start with this http://tuxthink.blogspot.com/2013/12/creating-directory-under-proc-in-kernel.html
Then look into kstrtol() function.
So, basically, I have a program that forks off a child process and creates a directory with the name + the child processes ID. This is done in another part of the code.
So, lets say the user names the directory "TestDir#". It will be TestDir12342 or something similar in the end.
So, later on, the user could enter a search term for that directory by typing in TestDir#. I want to lope off the "#", and have chdir() search for a directory that begins with that name, "TestDir". I don't have to worry about repeat files or similarly named files for this program.
Does anyone know a simple way to do this with chdir()? I have tried many different test code, but I am at a lose.
I have also attempted to store the child process ID in the parent process, but for some reason I can never get them to match. I am aware that fork() gives the child process ID in return to the parent. Yet, for some reason, the program refuses to make them match.
So, I am trying this as a workaround (searching the beginning part of the file name). Thanks for any assistance if someone knows of a way to do this.
readdir can be used to get the entries of the directory.
The following searchFirstDir finds the first prefix-matched directory. (tested in Ubuntu Linux)
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
int searchFirstDir(const char *workingDir, const char *prefix, char *resultBuffer, int bufferLen)
{
DIR *pDir = NULL;
int found = 0;
// opendir
{
pDir = opendir(workingDir);
if (pDir == NULL) {
perror("ERROR: opendir");
return -1;
}
}
// readdir
{
int ret;
struct dirent *pEntry;
struct dirent *result;
int prefixLen = strlen(prefix);
// refer: man readdir (in Linux)
{
long name_max = pathconf(workingDir, _PC_NAME_MAX);
if (name_max == -1) /* Limit not defined, or error */
name_max = 255; /* Take a guess */
size_t len = offsetof(struct dirent, d_name) + name_max + 1;
pEntry = malloc(len);
}
do {
ret = readdir_r(pDir, pEntry, &result);
if (ret) {
perror("ERROR: readdir_r");
break;
}
if (pEntry->d_type == DT_DIR && strncmp(pEntry->d_name, prefix, prefixLen) == 0) {
strncpy(resultBuffer, pEntry->d_name, bufferLen);
found++;
break;
}
} while(ret == 0 && result != NULL);
free(pEntry);
}
// closedir
closedir(pDir);
return found > 0 ? 0 : -1;
}
int main(int argc, char *argv)
{
char resultBuffer[255];
int ret = searchFirstDir("workingdirectory", "TestDir", resultBuffer, 255);
if (ret == 0) {
printf("First matched directory: %s\n", resultBuffer);
}
}
Yes, there is a way to perform the requested type of chdir taking advantage of globbing, i.e. filename expansion using a wildcard of "*", as follows:
#include <string.h>
#include <glob.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
/* Convert a wildcard pattern into a list of blank-separated
filenames which match the wildcard. */
char * glob_pattern(char *wildcard)
{
char *gfilename;
size_t cnt, length;
glob_t glob_results;
char **p;
glob(wildcard, GLOB_NOCHECK, 0, &glob_results);
/* How much space do we need? */
for (p = glob_results.gl_pathv, cnt = glob_results.gl_pathc;
cnt; p++, cnt--)
length += strlen(*p) + 1;
/* Allocate the space and generate the list. */
gfilename = (char *) calloc(length, sizeof(char));
for (p = glob_results.gl_pathv, cnt = glob_results.gl_pathc;
cnt; p++, cnt--)
{
strcat(gfilename, *p);
if (cnt > 1)
strcat(gfilename, " ");
}
globfree(&glob_results);
return gfilename;
}
int main() {
char *directory;
int ret;
directory = glob_pattern("te*");
ret = chdir (directory);
printf("Result of chdir: %d\n",ret);
}
Note: The "globbing" portion of the code comes from here
Linux has a glob utility so if you wish to do the same in C, you have to write the code yourself as this example portrays. When the program finishes however you will be back in the directory you originally used to run this script. When the code does a successful directory change, the return result is zero. Note, this code executed in a directory containing a subdirectory named "test".
I have a very simple modified driver to write to specific registers. I pass the value and the register I intend to write to. IE: 0x00000008 2, read as write 8 to register (base_addr + 2*4).
This works and was relatively straight forward to implement because the driver automatically receives an input buffer.
Now since in order to read I have to use the seq_file I'm not sure how to pass the register that I would like to read from... The augmented device driver I'm using is posted below.
Is there a way to pass the register value and then using ioread32(base_addr + register)? How can I use copy_from_user in this function where my only inputs are the seq_file?
Usage Example (altered ioread32 to be base_addr+2):
$ sudo echo "0x00001233 2" > /proc/accelerator
$ cat /proc/accelerator
0x1233
$
Dev Driver Code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h> /* Needed for copy_from_user */
#include <asm/io.h> /* Needed for IO Read/Write Functions */
#include <linux/proc_fs.h> /* Needed for Proc File System Functions */
#include <linux/seq_file.h> /* Needed for Sequence File Operations */
#include <linux/platform_device.h> /* Needed for Platform Driver Functions */
#include <linux/slab.h> /*for kmalloc and kfree */
#include <linux/vmalloc.h>
/* Define Driver Name */
#define DRIVER_NAME "accelerator"
unsigned long *base_addr; /* Vitual Base Address */
struct resource *res; /* Device Resource Structure */
unsigned long remap_size; /* Device Memory Size */
/* Write operation for /proc/accelerator
* -----------------------------------
* When user cat a string to /proc/accelerator file, the string will be stored in
* const char __user *buf. This function will copy the string from user
* space into kernel space, and change it to an unsigned long value.
* It will then write the value to the register of accelerator controller,
* and turn on the corresponding LEDs eventually.
*/
static ssize_t proc_accelerator_write(struct file *file, const char __user * buf,
size_t count, loff_t * ppos)
{
//Allocate
char * myaddr_phrase;
char * pEnd;
char *buffer = vzalloc(count);
myaddr_phrase = buffer;
u32 myaddr_value;
u32 myreg_value;
//Copy Data
if (count < 22) {
if (copy_from_user(myaddr_phrase, buf, count))
return -EFAULT;
//myaddr_phrase[count] = '\0';
printk("count = %d\n", count);
printk("%s\n",myaddr_phrase);
}
// Use strtol to parse input
/* http://www.cplusplus.com/reference/cstdlib/strtol/ */
myaddr_value = simple_strtoul(myaddr_phrase, &pEnd, 0);
printk("myaddr_value = %08x\n", myaddr_value);
pEnd = strsep(&myaddr_phrase," ");
myreg_value = simple_strtoul(myaddr_phrase,&pEnd ,0);
printk("myreg_value = %08x\n", myreg_value);
printk("final_value = %08x\n", (base_addr + (myreg_value)));
printk("mult_val = %08x\n", (myreg_value));
wmb();
iowrite32(myaddr_value, (base_addr + (myreg_value)));
return count;
}
/* Callback function when opening file /proc/accelerator
* ------------------------------------------------------
* Read the register value of accelerator controller, print the value to
* the sequence file struct seq_file *p. In file open operation for /proc/accelerator
* this callback function will be called first to fill up the seq_file,
* and seq_read function will print whatever in seq_file to the terminal.
*/
static int proc_accelerator_show(struct seq_file *p, void *v)
{
u32 accelerator_value;
accelerator_value = ioread32(base_addr+2);
seq_printf(p, "0x%x\n", accelerator_value);
return 0;
}
/* Open function for /proc/accelerator
* ------------------------------------
* When user want to read /proc/accelerator (i.e. cat /proc/accelerator), the open function
* will be called first. In the open function, a seq_file will be prepared and the
* status of accelerator will be filled into the seq_file by proc_accelerator_show function.
*
*p 69
int (*open) (struct inode *, struct file *);
Though this is always the first operation performed on the device file, the driver
is not required to declare a corresponding method. If this entry is NULL, opening
the device always succeeds, but your driver isn’t notified.
Open described on p76
*/
static int proc_accelerator_open(struct inode *inode, struct file *file)
{
unsigned int size = 16;
char *buf;
struct seq_file *m;
int res;
buf = (char *)kmalloc(size * sizeof(char), GFP_KERNEL);
if (!buf)
return -ENOMEM;
res = single_open(file, proc_accelerator_show, NULL);
if (!res) {
m = file->private_data;
m->buf = buf;
m->size = size;
} else {
kfree(buf);
}
return res;
}
/* File Operations for /proc/accelerator */
static const struct file_operations proc_accelerator_operations = {
.open = proc_accelerator_open,
.read = seq_read,
.write = proc_accelerator_write,
.llseek = seq_lseek,
.release = single_release
};
/*
int (*open) (struct inode *, struct file *);
Though this is always the first operation performed on the device file, the driver
is not required to declare a corresponding method. If this entry is NULL, opening
the device always succeeds, but your driver isn’t notified.
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
Used to retrieve data from the device. A null pointer in this position causes the
readsystem call to fail with-EINVAL(“Invalid argument”). A nonnegative return
value represents the number of bytes successfully read (the return value is a
“signed size” type, usually the native integer type for the target platform)
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
Sends data to the device. IfNULL, -EINVALis returned to the program calling the
writesystem call. The return value, if nonnegative, represents the number of
bytes successfully written.
loff_t (*llseek) (struct file *, loff_t, int);
Thellseek method is used to change the current read/write position in a file, and
the new position is returned as a (positive) return value. Theloff_tparameter is
a “long offset” and is at least 64 bits wide even on 32-bit platforms. Errors are
signaled by a negative return value. If this function pointer isNULL, seek calls will
modify the position counter in thefilestructure (described in the section “The
file Structure”) in potentially unpredictable ways.
int (*release) (struct inode *, struct file *);
This operation is invoked when thefilestructure is being released. Likeopen,
releasecan beNULL.
*
*/
/* Shutdown function for accelerator
* -----------------------------------
* Before accelerator shutdown, turn-off all the leds
*/
static void accelerator_shutdown(struct platform_device *pdev)
{
iowrite32(0, base_addr);
}
/* Remove function for accelerator
* ----------------------------------
* When accelerator module is removed, turn off all the leds first,
* release virtual address and the memory region requested.
*/
static int accelerator_remove(struct platform_device *pdev)
{
accelerator_shutdown(pdev);
/* Remove /proc/accelerator entry */
remove_proc_entry(DRIVER_NAME, NULL);
/* Release mapped virtual address */
iounmap(base_addr);
/* Release the region */
release_mem_region(res->start, remap_size);
return 0;
}
/* Device Probe function for accelerator
* ------------------------------------
* Get the resource structure from the information in device tree.
* request the memory region needed for the controller, and map it into
* kernel virtual memory space. Create an entry under /proc file system
* and register file operations for that entry.
*/
static int accelerator_probe(struct platform_device *pdev)
{
struct proc_dir_entry *accelerator_proc_entry;
int ret = 0;
printk(KERN_ALERT "Probing\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "No memory resource\n");
return -ENODEV;
}
remap_size = res->end - res->start + 1;
if (!request_mem_region(res->start, remap_size, pdev->name)) {
dev_err(&pdev->dev, "Cannot request IO\n");
return -ENXIO;
}
base_addr = ioremap(res->start, remap_size);
if (base_addr == NULL) {
dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n",
(unsigned long)res->start);
ret = -ENOMEM;
goto err_release_region;
}
accelerator_proc_entry = proc_create(DRIVER_NAME, 0, NULL,
&proc_accelerator_operations);
if (accelerator_proc_entry == NULL) {
dev_err(&pdev->dev, "Couldn't create proc entry\n");
ret = -ENOMEM;
goto err_create_proc_entry;
}
printk(KERN_INFO DRIVER_NAME " probed at VA 0x%08lx\n",
(unsigned long) base_addr);
printk(KERN_ALERT "Goodbye, probe\n");
return 0;
err_create_proc_entry:
iounmap(base_addr);
err_release_region:
release_mem_region(res->start, remap_size);
return ret;
}
/* device match table to match with device node in device tree */
/*
https://lwn.net/Articles/448502/
*/
static const struct of_device_id accelerator_of_match[] = {
{.compatible = "PCA,bitSplitter"},
{},
};
MODULE_DEVICE_TABLE(of, accelerator_of_match);
/* platform driver structure for accelerator driver */
/*
Platform devices are represented by the struct, and is found in <linux/platform_device.h>
at minimum probe() and remove() must be supplied, the others have to do with power management
https://lwn.net/Articles/448499/
*/
static struct platform_driver accelerator_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = accelerator_of_match},
.probe = accelerator_probe,
.remove = accelerator_remove,
.shutdown = accelerator_shutdown
};
/* Register accelerator platform driver */
/*
Helper macro for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit()
*
* Platform drivers are for HW that will not dynamically come and go into a Linux system,
* such as the video and audio controllers in a tablet. In makes sense to statically pull
* in those code necessary through the __initcall magic discussed above.
*
* http://henryomd.blogspot.com/2014/11/linux-kernel-startup.html
*/
module_platform_driver(accelerator_driver);
/* Module Informations */
/*
Discussed in 2.6 Preliminaries
*/
MODULE_AUTHOR("Digilent, Inc.");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRIVER_NAME ": accelerator driver (Simple Version)");
MODULE_ALIAS(DRIVER_NAME);
The system call write it's defined as follow:
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_write(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
I'd like to copy the variable buf to modify your content and
then use this new variable at:
vfs_write(file, new_buf, count, &pos);
I've tried to allocate memory to a char pointer variable with kmalloc and then I've used copy_from_user() to do the copy. Finally I've used the new variable at vfs_write(). After recompile the kernel and reboot the system I've got kernel panic error message.
Here is my implementation that generates a kernel panic error message:
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count){
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
char *data;
data = kmalloc(count, GFP_KERNEL);
if(!data)
return ret;
copy_from_user(data, buf, count);
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_write(file, data, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
How can I do this copy in kernel mode?
I'm using Linux Mint 12 - Kernel version: 3.0.30
You should probably also post your code. I.e. the changes you made to the write system call to be certain where the error is.
That said, there are checks in place that don't allow you to use kernel memory for system calls. You either need to allocate your buffer in user address space for the process (bad) or disable the checks (not as bad).
I'm not as familiar with the 3.0 kernel but this answer looks promising:
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
/* Your syscall here */
set_fs(old_fs);