I'm trying to build a small demonstration kernel module for Linux which finds a specific process and reads a value from that process' memory.
I have put together the following code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/highmem.h>
#include <asm/uaccess.h>
static void read_process_memory(struct task_struct *task) {
char buf[1024];
unsigned long size;
unsigned long ret;
void __user *addr;
struct mm_struct *mm = get_task_mm(task);
struct vm_area_struct *vma;
if (!mm) {
// Abort
return;
}
// Lock for reading
down_read(&mm->mmap_sem);
vma = mm->mmap;
memset(buf, 0, 1024);
// Make sure read is enabled for this region
if (vma && vma->vm_flags & VM_READ) {
// Read without overflowing
size = vma->vm_end - vma->vm_start;
if (size > 1023) {
size = 1023;
}
// Attempt to get the data from the start of the vma
addr = (void __user *)vma->vm_start;
if (access_ok(VERIFY_READ, addr, size)) {
ret = copy_from_user(buf, addr, size);
if (ret == 0) {
// Probably doesn't contain anything relevent
printk(KERN_ALERT "mymodule: Read '%s'\n", buf);
} else {
printk(KERN_ALERT "mymodule: Failed to copy %lu bytes from userspace\n", ret);
}
} else {
printk(KERN_ALERT "mymodule: access_ok check failed\n");
}
// Release the lock
up_read(&mm->mmap_sem);
mmput(mm);
}
}
static int __init mymodule_init(void) {
struct task_struct *task;
bool found = false;
printk(KERN_ALERT "mymodule: Starting\n");
// Find the process
rcu_read_lock();
for_each_process(task) {
if (strcmp(task->comm, "example-process") == 0) {
printk(KERN_ALERT "mymodule: Found pid %d for process %s\n", task->pid, task->comm);
found = true;
break;
}
}
rcu_read_unlock();
if (!found) {
printk(KERN_ALERT "mymodule: Process not found, aborting\n");
return -1;
}
read_process_memory(task);
return 0;
}
static void __exit mymodule_exit(void) {
printk(KERN_ALERT "mymodule: Stopped\n");
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("user");
Everything works fine until the copy_from_user call, which returns a non-zero count (actually, it returns size, meaning it didn't ready any data), regardless of the process and vma I try to read from.
Is there a misunderstanding on my part, or anything I'm doing wrong?
EDIT:
Here's a working version of read_process_memory:
static void read_process_memory(struct task_struct *task) {
char buf[1024];
unsigned long size;
int ret;
void *addr;
struct mm_struct *mm = get_task_mm(task);
struct vm_area_struct *vma;
struct page *pages[1];
if (!mm) {
// Abort
return;
}
// Lock for reading
down_read(&mm->mmap_sem);
vma = mm->mmap;
memset(buf, 0, 1024);
// Make sure read is enabled for this region
if (vma && vma->vm_flags & VM_READ) {
// Get the first page
ret = get_user_pages_remote(task, mm, vma->vm_start, 1, FOLL_FORCE, pages, NULL);
if (ret > 0) {
// We got our page, we should now be able to map it and read directly
addr = kmap(*pages);
// Read without overflowing
size = vma->vm_end - vma->vm_start;
if (size > 1023) {
size = 1023;
}
strncpy(buf, addr, size);
// Probably doesn't contain anything relevent
printk(KERN_ALERT "mymodule: Read '%s'\n", buf);
// Make sure to release our page
kunmap(*pages);
put_page(*pages);
} else {
printk(KERN_ALERT "mymodule: Failed to read page at %p (errno=%d)\n", (void *)vma->vm_start, ret);
}
// Release the lock
up_read(&mm->mmap_sem);
mmput(mm);
}
}
I'd say your code is wrong. Please be noted you are accessing memory of remote process (not current). To do that correctly you must use one of the GUP APIs(e.g. get_user_pages_remote()) to grab the pages first. The uaccess APIs (e.g. copy_from_user) only works for current process.
Here is an example in iouring:
https://elixir.bootlin.com/linux/latest/source/fs/io_uring.c#L4839
Related
I'm working on a device driver that needs to preform mem to mem copies via dma on a Beaglebone Black (ARM) with Linux Kernel 5.4.106. So far I've managed to successfully request a mem to mem compatible channel (and release it when I'm done), but I'm unable to perform the actual transfer.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dmaengine.h>
struct dma_chan *chan;
void *src;
void *dst;
static int __init mod_init(void)
{
dma_cap_mask_t mask;
int ret;
struct dma_async_tx_descriptor *tx = NULL;
dma_cookie_t cookie;
int *writer;
enum dma_status status;
printk("mod_init called\n");
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
chan = dma_request_channel(mask, NULL, NULL);
if(!chan){
printk("no mem2mem channels available");
ret = -EAGAIN;
goto fail_chan;
}
printk("requested channel");
src = kzalloc(16,GFP_KERNEL);
if(src == NULL){
ret = -ENOMEM;
goto fail_m1;
}
dst = kzalloc(16,GFP_KERNEL);
if(dst == NULL){
ret = -ENOMEM;
goto fail_m2;
}
writer = (int *)src;
*writer = 20;
tx = chan->device->device_prep_dma_memcpy(chan, virt_to_phys(dst), virt_to_phys(src), 16, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
if (!tx) {
printk("prep error");
}
printk("slave configured");
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
printk("submit error");
}
dma_async_issue_pending(chan);
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
if(status != DMA_COMPLETE){
printk("something went wrong");
}
printk("dst: %d, src: %d", *(int *)dst, *(int *)src);
printk("done");
return 0;
fail_m2:
kfree(src);
fail_m1:
dma_release_channel(chan);
fail_chan:
return ret;
}
static void __exit mod_exit(void)
{
printk("mod_exit called\n");
dma_release_channel(chan);
printk("dst: %d, src: %d", *(int *)dst, *(int *)src);
kfree(src);
kfree(dst);
printk("released channel");
}
module_init( mod_init );
module_exit( mod_exit );
MODULE_AUTHOR("Me");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA engine test.");
This kernel module compiles without any issues and when installed does not cause any errors. Eventho the cookie returns a DMA_COMPLETE status, the value in dst stays 0 (it should become 20).
Most of the code is based on the dmatest driver, which preforms flawlessly, but my copy is missing the mark.
What could be the issue here? Am I missing a step?
Try adding GFP_DMA flag to your kzalloc() calls - the memory you allocate should be suitable for DMA transfers, that is platform-dependent.
I'm working on implementing a variant of https://apenwarr.ca/log/20190216. Long story short, the main idea is to have a space in memory where to keep informations and to retrieve this information after a soft reboot/panic.
In my case, I just want to keep some variables from a reboot to another. So I've worked on a simple variant of this mechanism to do the job. The code is simply a copy paste from the original patch with some raw adaptations. I've added a syscall to enter kernel mode to execute this code (not shown here).
struct logbits {
int magic; /* needed to verify the memory across reboots */
int state;
int nb_reboot;
};
#define PERSIST_SEARCH_START 0
#ifdef CONFIG_NO_BOOTMEM
#define PERSIST_SEARCH_END 0x5e000000
#else
#define PERSIST_SEARCH_END 0xfe000000
#endif
#define PERSIST_SEARCH_JUMP (4*1024)
#define PERSIST_MAGIC 0xba5eba11
/*
* arm uses one memory model, mips uses another
*/
phys_addr_t physmem_reserve(phys_addr_t size) {
#ifdef CONFIG_NO_BOOTMEM
phys_addr_t alloc;
alloc = memblock_find_in_range_node(size, SMP_CACHE_BYTES,
PERSIST_SEARCH_START, PERSIST_SEARCH_END,
NUMA_NO_NODE);
if (!alloc) return alloc;
if (memblock_reserve(alloc, size)) {
pr_err("info_keeper: memblock_reserve failed\n");
return 0;
}
return alloc;
#else
unsigned long where;
for (where = PERSIST_SEARCH_END - size;
where >= PERSIST_SEARCH_START && where <= PERSIST_SEARCH_END - size;
where -= PERSIST_SEARCH_JUMP) {
if (reserve_bootmem(where, size, BOOTMEM_EXCLUSIVE))
continue;
else
return where;
}
return 0;
#endif
}
struct logbits *log_buf_alloc(char **new_logbuf)
{
char *buf;
phys_addr_t alloc;
unsigned long size = sizeof(struct logbits);
unsigned long full_size = size;
struct logbits *new_logbits;
alloc = physmem_reserve(full_size);
if (alloc) {
printk(KERN_INFO "info_keeper: memory reserved # 0x%08x\n", alloc);
buf = phys_to_virt(alloc);
if(buf){
*new_logbuf = buf;
new_logbits = (void*)buf;
printk(KERN_INFO "info_keeper: memory virtual # 0x%08x\n", buf);
if (new_logbits->magic != PERSIST_MAGIC) {
printk(KERN_INFO "info_keeper: header invalid, " "cleared.\n");
memset(buf, 0, full_size);
memset(new_logbits, 0, sizeof(*new_logbits));
new_logbits->magic = PERSIST_MAGIC;
} else {
printk(KERN_INFO "info_keeper: header valid; " "state=%d\n" "nb_reboot=%d\n", new_logbits->state, new_logbits->nb_reboot);
}
return new_logbits;
}else{
printk(KERN_ERR "info_keeper: failed to get phys to virt");
buf = alloc_bootmem(full_size);
*new_logbuf = buf;
new_logbits = (struct logbits*)(buf);
memset(buf, 0, full_size);
}
} else {
/* replace the buffer */
printk(KERN_ERR "info_keeper: failed to reserve bootmem " "area. disabled.\n");
buf = alloc_bootmem(full_size);
*new_logbuf = buf;
new_logbits = (struct logbits*)(buf);
memset(buf, 0, full_size);
}
return new_logbits;
}
Upon execution, the physmem_reserve function is successful and returns a memory region. Then I get a physical to virtual memory mapping from phys_to_virt. Then, when I try to access the memory, I get this Unable to handle kernel paging request at virtual address error.
Here is a sample output :
[ 42.489639] info_keeper: memory reserved # 0x5dffffc0
[ 42.494781] info_keeper: memory virtual # 0x0dffffc0
[ 42.499778] Unable to handle kernel paging request at virtual address 0dffffc0
Any idea on what is happening ?
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#define DEVICE_NAME "kbdozgur"
#define CLASS_NAME "kbdozgur"
MODULE_AUTHOR("Mehmet Ozgur Bayhan");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Interrupt buffering skeleton");
MODULE_VERSION("0.2");
#define BUFFER_SIZE 20
static unsigned char bfr[BUFFER_SIZE];
static int bufferCounter = 0;
static int majorNumber;
static char message[BUFFER_SIZE] = { 0 };
static short size_of_message;
static int numberOpens = 0;
static struct class* kbdozgurcharClass = NULL;
static struct device* kbdozgurcharDevice = NULL;
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, };
irq_handler_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) {
static unsigned char scancode;
//Read keyboard status
scancode = inb(0x60);
if (scancode == 0x01) {
printk(KERN_INFO "MOB: Inputs are > %s\n", bfr);
bufferCounter = 0;
memset(&bfr[0], 0, sizeof(bfr));
}
else if (scancode == 0x1E) {
bfr[bufferCounter] = 'a';
bufferCounter++;
}
else if (scancode == 0x1F) {
bfr[bufferCounter] = 's';
bufferCounter++;
}
else if (scancode == 0x20) {
bfr[bufferCounter] = 'd';
bufferCounter++;
}
else if (scancode == 0x21) {
bfr[bufferCounter] = 'f';
bufferCounter++;
}
else if (scancode == 0x22) {
bfr[bufferCounter] = 'g';
bufferCounter++;
}
else if (scancode == 0x23) {
bfr[bufferCounter] = 'h';
bufferCounter++;
}
else if (scancode == 0x24) {
bfr[bufferCounter] = 'j';
bufferCounter++;
}
if (bufferCounter >= BUFFER_SIZE) {
bufferCounter = 0;
memset(&bfr[0], 0, sizeof(bfr));
}
return (irq_handler_t) IRQ_HANDLED;
}
static int init_mod(void) {
int result;
/*
*****************************
* Create Character device
*****************************
*/
// Try to dynamically allocate a major number for the device
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber < 0) {
printk(KERN_ALERT "MOB: kbdozgurcharClass failed to register a major number\n");
return majorNumber;
}
printk(KERN_INFO "MOB: registered correctly with major number %d\n", majorNumber);
// Register the device class
kbdozgurcharClass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(kbdozgurcharClass)) { // Check for error and clean up if there is
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "MOB: Failed to register device class\n");
return PTR_ERR(kbdozgurcharClass); // Correct way to return an error on a pointer
}
printk(KERN_INFO "MOB: device class registered correctly\n");
// Register the device driver
kbdozgurcharDevice = device_create(kbdozgurcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(kbdozgurcharDevice)) { // Clean up if there is an error
class_destroy(kbdozgurcharClass); // Repeated code but the alternative is goto statements
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "MOB: Failed to create the device\n");
return PTR_ERR(kbdozgurcharDevice);
}
printk(KERN_INFO "MOB: device class created correctly\n"); // Made it! device was initialized
/*
*****************************
* Bind interrupt
*****************************
*/
result = request_irq(1, (irq_handler_t) irq_handler, IRQF_SHARED, "kbdozgur", (void *) (irq_handler));
if (result) printk(KERN_INFO "MOB: can't get shared interrupt for keyboard\n");
printk(KERN_INFO "MOB: kbdozgur loaded.\n");
return result;
}
static void exit_mod(void) {
/*
* ****************************
* Destroy Character Device
* ****************************
*/
device_unregister(kbdozgurcharDevice);
device_destroy(kbdozgurcharClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(kbdozgurcharClass); // unregister the device class
class_destroy(kbdozgurcharClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "MOB: Goodbye from the LKM!\n");
/*
* ****************************
* Free IRQ bind
* ****************************
*/
free_irq(1, (void *) (irq_handler));
printk(KERN_INFO "MOB: kbdozgur unloaded.\n");
}
static int dev_open(struct inode *inodep, struct file *filep) {
numberOpens++;
printk(KERN_INFO "MOB: 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);
error_count = copy_to_user(buffer, "test", 4);
if (error_count == 0) { // if true then have success
// printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", size_of_message, message);
printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", 4, "test");
return (size_of_message = 0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO "MOB: 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(%d letters)", buffer, len); // appending received string with its length
size_of_message = strlen(message); // store the length of the stored message
printk(KERN_INFO "MOB: Received %d characters from the user\n", len);
return len;
}
static int dev_release(struct inode *inodep, struct file *filep) {
printk(KERN_INFO "MOB: Device successfully closed\n");
return 0;
}
module_init(init_mod);
module_exit(exit_mod);
I am trying to build a skeleton driver for interrupt buffering and serving to userspace.
However my character device returns empty string to user space. I tried cat in shell and open and read in python. Both returns empty string. By the way it takes character array from user space normally and as i expected.
Related part >>
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);
error_count = copy_to_user(buffer, "test", 4);
if (error_count == 0) { // if true then have success
// printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", size_of_message, message);
printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", 4, "test");
return (size_of_message = 0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO "MOB: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
First i tried:
error_count = copy_to_user(buffer, message, size_of_message);
Then i tried for check:
error_count = copy_to_user(buffer, "test", 4);
Same story. Both returns empty string. There is no error. Nothing related in dmesg.
I make my trials as root user and the file has that permissions:
crw------- 1 root root 250, 0 Mar 30 14:43 /dev/kbdozgur
So where did i do wrong?
The read should return the number of bytes read. In your case, you are return 0.
size_message = 0
You should do the following
size_t size_requested;
...
if (len >= size_of_message) {
size_requested = size_of_message;
} else {
size_requested = len;
}
if (copy_to_user (buf, message, size_requested)) {
retval = -EFAULT;
return retval
}
return size_requested;
In following kernel module, I hooked syscall sys_open, and now trying to send filename to process in userspace using Netlink socket, in response process will return a msg, and then according to msg, the kernel module will proceed further.
source code: foo.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
#include <linux/syscalls.h>
#include <linux/delay.h> // loops_per_jiffy
//===============netlink=================
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#define NETLINK_USER 31
struct sock *nl_sk = NULL;
//===============netlink=================
#define CR0_WP 0x00010000 // Write Protect Bit (CR0:16)
/* Just so we do not taint the kernel */
MODULE_LICENSE("GPL");
void **syscall_table;
unsigned long **find_sys_call_table(void);
long (*orig_sys_open)(const char __user *filename, int flags, int mode);
//===============netlink=================
static void hello_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr *)skb->data;
printk(KERN_INFO "Netlink received msg payload: %s\n", (char *)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; /*pid of sending process */
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0)
printk(KERN_INFO "Error while sending bak to user\n");
}
//===============netlink=================
unsigned long **find_sys_call_table()
{
unsigned long ptr;
unsigned long *p;
for (ptr = (unsigned long)sys_close;
ptr < (unsigned long)&loops_per_jiffy;
ptr += sizeof(void *))
{
p = (unsigned long *)ptr;
if (p[__NR_close] == (unsigned long)sys_close)
{
printk(KERN_DEBUG "Found the sys_call_table!!!\n");
return (unsigned long **)p;
}
}
return NULL;
}
long my_sys_open(const char __user *filename, int flags, int mode)
{
long ret;
//Send filename & get response from user space app
if(/*user_space_response ==*/ 0)
{
/*Other processing*/
}
ret = orig_sys_open(filename, flags, mode);
printk(KERN_DEBUG "file %s has been opened with mode %d\n", filename, mode);
return ret;
}
static int __init syscall_init(void)
{
int ret;
unsigned long addr;
unsigned long cr0;
syscall_table = (void **)find_sys_call_table();
if (!syscall_table)
{
printk(KERN_DEBUG "Cannot find the system call address\n");
return -1;
}
//===============netlink=================
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
if (!nl_sk)
{
printk(KERN_DEBUG "Error creating socket.\n");
return -1;
}
//===============netlink=================
cr0 = read_cr0();
write_cr0(cr0 & ~CR0_WP);
addr = (unsigned long)syscall_table;
ret = set_memory_rw(PAGE_ALIGN(addr) - PAGE_SIZE, 3);
if(ret)
{
printk(KERN_DEBUG "Cannot set the memory to rw (%d) at addr %16lX\n", ret, PAGE_ALIGN(addr) - PAGE_SIZE);
}
else
{
printk(KERN_DEBUG "3 pages set to rw");
}
orig_sys_open = syscall_table[__NR_open];
syscall_table[__NR_open] = my_sys_open;
write_cr0(cr0);
return 0;
}
static void __exit syscall_release(void)
{
unsigned long cr0;
cr0 = read_cr0();
write_cr0(cr0 & ~CR0_WP);
syscall_table[__NR_open] = orig_sys_open;
write_cr0(cr0);
netlink_kernel_release(nl_sk);
}
module_init(syscall_init);
module_exit(syscall_release);
The function 'hello_nl_recv_msg' which is a callback function sends and receives msgs to the process but How can I send msg (i.e. filename) from function 'my_sys_open' to process in user space? and how to wait for response?
Makefile :
obj-m += foo.o
all:
make -C /usr/src/linux-headers-3.2.0-23-generic/ M=$(PWD) modules
clean:
make -C /usr/src/linux-headers-3.2.0-23-generic/ M=$(PWD) clean
Thanks for your time ;)
How can I send msg (i.e. filename) from function 'my_sys_open' to process in user space?
User-space program should create socket AF_NETLINK, address of this socket will be used to send message to it. For detailed info read man netlink.
and how to wait for response?
You can use any standard mechanism for make my_sys_open waiting responce event in hello_nl_recv_msg, e.g. wait_event. Simplified code:
/*
* Whether responce is recieved.
*
* For process concurrent open's this should be map,
* e.g., struct task_struct -> bool.
*/
int have_responce = 0;
DECLARE_WAIT_QUEUE_HEAD(responce_waitqueue); // Waitqueue for wait responce.
static void hello_nl_recv_msg(struct sk_buff *skb)
{
...
if(<detect responce from user program>)
{
have_responce = 1;
wake_up_all(responce_waitqueue);
}
...
}
long my_sys_open(const char __user *filename, int flags, int mode)
{
struct sk_buff *skb_out;
...
have_responce = 0; // clear responce flag
nlmsg_unicast(nl_sk, skb_out, <stored_user_pid>);// send message
wait_event(responce_waitqueue, have_responce); //wait until responce is received
....
}
I am trying to implement a FIFO using character driver. However while writing to the device it doesn't seem to work. It doesn't seems to end the loop. Any help or link is appreciated. I have taken help from many sources so current code is kind of mess with many things which shouldn't be as they are.
static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off) {
int mode;
int ind;
ssize_t count = -ENOMEM;
printk(KERN_ALERT "to be written : %s\n", buff);
mode = iminor(filp->f_dentry->d_inode);
printk(KERN_ALERT "Device minor : %d\n", mode);
if ((mode == 1) || (mode ==3))
return -EINVAL;
if (mode == 0){
count = 0;
ind = 0;
if (buff[ind] == NULL) {
return -ENOMEM;
}
printk(KERN_ALERT "Write position1 : %d\n", writePos1);
while(ind<=len) { //loop untill we have something to writer
if (down_interruptible(&buffer1_e)) { //taking flag first isn't right because that won't allow other guyto give access to our turn.
printk(KERN_ALERT "buffer1 flag didn't work\t %d", buffer1_e.count);
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag1)){
up(&buffer1_e); //must because we couldn't write it properly
return -EINVAL;
}
else {
queue1[writePos1] = buff[ind];
printk(KERN_ALERT "Write %d %c\n",ind,queue1[writePos1]);
if (writePos1 == 9){
writePos1 = 0;
}
else
writePos1++;
count++;
}
up(&flag1);
}
up(&buffer1_f);
off += count;
ind++;
}
printk(KERN_ALERT "Write position1 now: %d\t and count%d\n", writePos1,count);
return count-1;
}
I just wrote my own module and I suspect that your issue is that the process calling dev_write expects dev_write to return the number of bytes written. If you don't return the correct number (I see you are returning count - 1), dev_write will be called again and again.
dev_read I have found to be similar - until it returns 0, the process will repeatedly call it - expecting that there are more characters to be retrieved (this makes sense).
I have written/modified a much more simple module that illustrates using a module as a character buffer (sorry, it's quite hastily written). It should allow you to echo a string to it, and return that string when you cat or otherwise read from it. This is demonstrated when you make run. I am sure you will be able to easily modify it to become FIFO.
I don't accept any liability for crashing your kernel or other problems, and you should be using a VM anyway.
It is long, so on my github:
git clone https://github.com/n-hutton/tempRepo
cd tempRepo
make
make run
Finally I corrected complete code. It is very much immature, illogical for few logics still it works the I would like to do it. #Nathan Hutton helped a lot in improving it and making it work. I thank him for that. Though there are many things I still have one major doubt, if you could trace it in kernel logs, an extra line feed character is added every time whenever you write to a device with (echo "test" |cat >/dev/fifo0)
to run it properly you will also need to create 4 char devices with Major number 240, minor 0,1,2,3. mknod can be used as:
"mknod /dev/fifo(0,1,2,3) c 240 (0,1,2,3) -m 777" //0,1,2,3 at a time and 777 can be modified for granularity. Finally working code:
#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <linux/semaphore.h>
#define DEVNO 240
#define DEVNAME "fifo"
MODULE_LICENSE("GPL");
DECLARE_WAIT_QUEUE_HEAD(writing1);
//static short writeFlag1=0;
static DEFINE_SEMAPHORE(flag1);
static DEFINE_SEMAPHORE(flag2);
static struct semaphore buffer1_f;
static struct semaphore buffer2_f;
static struct semaphore buffer1_e;
static struct semaphore buffer2_e;
DECLARE_WAIT_QUEUE_HEAD(writing2);
//static short writeFlag2=0;
static char queue1[10]={0};
static short readPos1=0;
static short writePos1=0;
//static short qsize1=0;
static char queue2[10]={0};
static short readPos2=0;
static short writePos2=0;
//static short qsize2=0;
static int times=0;
static int dev_open(struct inode *,struct file *);
static int dev_rls(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={
.read=dev_read,
.write=dev_write,
.open=dev_open,
.release=dev_rls,
};
int init_module(void){
unsigned int devno = 240;
char *devname = "fifo";
int t;
sema_init(&buffer1_f,0);
sema_init(&buffer2_f,0);
sema_init(&buffer1_e,10);
sema_init(&buffer2_e,10);
memset(queue1,0,10);
memset(queue2,0,10);
t=register_chrdev(devno,devname,&fops);
if(t<0) printk(KERN_ALERT "device reg failed. \n");
else printk(KERN_ALERT "Device registered. \n");
return t;
}
void cleanup_module(void) {
unregister_chrdev(240,"fifo");
printk(KERN_ALERT "Device has been removed");
}
static int dev_open(struct inode *inod, struct file *fil){
times++;
printk(KERN_ALERT "Device opened %d times\n",times);
return 0;
}
static ssize_t dev_read(struct file *filep, char *buff, size_t len, loff_t *off) {
int mode = iminor((filep->f_dentry->d_inode));
short count;
printk(KERN_ALERT "Device minor when read : %d\n", mode);
if ((mode == 0) || (mode ==2))
return -EINVAL;
else if (mode == 1){
count = 0;
printk(KERN_ALERT "Read position1 when read: %d\n", readPos1);
while(len) { //loop untill we have something to write or empty buffer
if (readPos1==writePos1){
printk(KERN_ALERT "Returning chars put to buffer: %d\n", count);
return count;
}
if (down_interruptible(&buffer1_f)) {
printk(KERN_ALERT "flag1 didn't work");
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag1)){
return -EINVAL;
}
else {
printk(KERN_ALERT "Read %c\n",queue1[readPos1]);
put_user(queue1[readPos1],buff++);
if (writePos1==-1) writePos1=readPos1;
if (readPos1 == 9) readPos1 = 0;
else readPos1++;
count++;
}
up(&flag1);
}
up(&buffer1_e);
}
printk(KERN_ALERT "Read position1 now: %d\t and count%d\n", readPos1,count);
return count;
}
else if (mode == 3){
count = 0;
printk(KERN_ALERT "Read position2 when read: %d\n", readPos2);
while(len) { //loop untill we have something to write or empty buffer
if (readPos2==writePos2){
printk(KERN_ALERT "Returning chars put to buffer: %d\n", count);
return count;
}
if (down_interruptible(&buffer2_f)) {
printk(KERN_ALERT "flag2 didn't work");
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag2)){
return -EINVAL;
}
else {
printk(KERN_ALERT "Read %c\n",queue2[readPos2]);
put_user(queue2[readPos2],buff++);
if (writePos2==-1) writePos2=readPos2;
if (readPos2 == 9) readPos2 = 0;
else readPos2++;
count++;
}
up(&flag2);
}
up(&buffer2_e);
}
printk(KERN_ALERT "Read position2 now: %d\t and count%d\n", readPos2,count);
return count;
}
else {
printk(KERN_ALERT "Not correct mode\n");
return -1;
}
}
static char Message[100] = "Initial message\n";
static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off) {
int mode;
int ind;
ssize_t count = -ENOMEM;
int i;
//Let's copy the message onto our stack so we can be clear what we are getting
for (i = 0; i < 99 && i < len; i++){
char getChar;
get_user(getChar, buff + i);
Message[i] = getChar;
}
Message[i] = '\0';
printk(KERN_ALERT "to be written : %s\n", Message);
mode = iminor(filp->f_dentry->d_inode);
printk(KERN_ALERT "Device minor : %d\n", mode);
if ((mode == 1) || (mode ==3))
return -EINVAL;
else if (mode == 0){
count = 0;
ind = 0;
if (( buff == NULL) || (*buff == 0)) {
return -ENOMEM;
}
printk(KERN_ALERT "Write position1 : %d\n", writePos1);
while(ind<len) { //loop untill we have something to writer
if (down_interruptible(&buffer1_e)) { //taking flag first isn't right because that won't allow other guyto give access to our turn.
printk(KERN_ALERT "buffer1 flag didn't work\t %d", buffer1_e.count);
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag1)){
up(&buffer1_e); //must because we couldn't write it properly
return -EINVAL;
}
else {
queue1[writePos1] = buff[ind];
printk(KERN_ALERT "Write ind:%d writepos:%d readpos;%d char:%c\tascii%d\n",ind,writePos1,readPos1,queue1[writePos1],(int)queue1[writePos1]);
if (readPos1==((writePos1+1)%10)) {
writePos1=-1;
}
else if (writePos1 == 9){
writePos1 = 0;
}
else
writePos1++;
count++;
}
printk(KERN_ALERT "writepos:%d",writePos1);
up(&flag1);
}
up(&buffer1_f);
off += count;
ind++;
}
printk(KERN_ALERT "Write position1 now: %d\t and count%d\n", writePos1,(int)count);
printk(KERN_ALERT "Note: our allowable buffer length was %d\n", (int)len);
return count;
}
else if (mode == 2){
count = 0;
ind = 0;
if (( buff == NULL) || (*buff == 0)) {
return -ENOMEM;
}
printk(KERN_ALERT "Write position2 : %d\n", writePos2);
while(ind<len) { //loop untill we have something to writer
if (down_interruptible(&buffer2_e)) { //taking flag first isn't right because that won't allow other guyto give access to our turn.
printk(KERN_ALERT "buffer2 flag didn't work\t %d", buffer2_e.count);
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag2)){
up(&buffer2_e); //must because we couldn't write it properly
return -EINVAL;
}
else {
queue2[writePos2] = buff[ind];
printk(KERN_ALERT "Write ind:%d writepos2:%d readpos2;%d char:%c\tascii%d\n",ind,writePos2,readPos2,queue2[writePos2],(int)queue2[writePos2]);
if (readPos2==((writePos2+1)%10)) {
writePos2=-1;
}
else if (writePos2 == 9){
writePos2 = 0;
}
else
writePos2++;
count++;
}
printk(KERN_ALERT "writepos:%d",writePos2);
up(&flag2);
}
up(&buffer2_f);
off += count;
ind++;
}
printk(KERN_ALERT "Write position2 now: %d\t and count%d\n", writePos2,(int)count);
printk(KERN_ALERT "Note: our allowable buffer length was %d\n", (int)len);
return count;
}
else {
printk(KERN_ALERT "This meant wrong device minor accessed\n");
return -1;
}
}
static int dev_rls(struct inode *inod, struct file *fil) {
printk(KERN_ALERT "Device is closed\n");
return 0;
}