Kernel Module Snippets – Part 2 – Hello World Continued

Kernel Module Snippets – Part 2 – Hello World Continued

Intro

In the last post, I mentioned the /proc system but never got much further. We’ll delve a little deeper in this post.

So what is /proc anyway? According to wikipedia – “The proc file is a special file system … that acts as an interface to internal data structures in the kernel. It can be used to obtain information about the system and to change certain kernel parameters at runtime (sysctl).”

We’ll leverage this interface as a way to interact with our “Hello, World” kernel module.

The Code

We’ll jump right into our example again, except this time we’re going to work with a main.c file and our new proc.c file.

Firstly the main.c is mostly a copy of the hello.c from the previous post, but with a few additions.

   
  


\#include    /\* We're doing Kernel work \*/

\#include    /\* Specifically a Kernel Module \*/

\#include      /\* Needed for the module_init/exit() macros \*/

\#include "hello.h"

static int __init hello_init(void)

{

    int rc;  

    printk(KERN_INFO "Hello, world!\n");

    /\* create the /proc file \*/

    rc = my_proc_init();

    if (rc == -1)

        return -1;

    return 0;

}

static void __exit hello_exit(void)

{

    my_proc_cleanup();

    printk(KERN_INFO "Goodbye, world!\n");

}

module_init(hello_init);

module_exit(hello_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Rob Hartzenberg ");

MODULE_DESCRIPTION("Hello world example.");

MODULE_SUPPORTED_DEVICE("hello");


  

Here comes the fun part – the /proc handling.

   
  

\#include  /\* We're doing kernel work \*/

\#include  /\* Specifically, a module \*/

\#include     /\* Necessary because we use the proc fs \*/

\#include   /\* for copy_from_user \*/

\#include 

\#include 

\#include 

\#include "hello.h"

\#define PROCFS_MAX_SIZE     1024

\#define PROCFS_NAME         "hello"

static char procfs_buffer\[PROCFS_MAX_SIZE];

static unsigned long procfs_buffer_size = 0;

static struct proc_dir_entry *entry;

static struct proc_dir_entry *parent;

/\*\*

\* This function is called when the /proc file is read.

\*/

static ssize_t procfile_read(struct file \*file, char __user \*buffer, size_t count, loff_t *offset)

{

    if (\*offset > 0 || count < PROCFS_MAX_SIZE) /\* we have finished to read, return 0 */

        return 0;

    /\* fill the buffer, return the buffer size \*/

    if(copy_to_user(buffer, procfs_buffer, procfs_buffer_size))

        return -EFAULT;

\*offset = procfs_buffer_size;

    return procfs_buffer_size;

}

/\*\*

\* This function is called when the /proc file is written

\*/

static ssize_t procfile_write(struct file\* file,const char __user \*buffer,size_t count,loff_t *f_pos){

    int tlen;

    char *tmp = kzalloc((count+1),GFP_KERNEL);

    if(!tmp)return -ENOMEM;

    if(copy_from_user(tmp,buffer,count)){

        kfree(tmp);

        return EFAULT;

    }

    tlen = PROCFS_MAX_SIZE;

    if (count < PROCFS_MAX_SIZE)

        tlen = count;

    memcpy(&procfs_buffer,tmp,tlen);

    procfs_buffer_size = tlen;

    kfree(tmp);

    return tlen;

}

static int procfile_show(struct seq_file \*m,void \*v){

    static char *str = NULL;

    seq_printf(m,"%s\n",str);

    return 0;

}

/\*\*

\* Open the procfile

\*/

static int procfile_open(struct inode \*inode,struct file \*file){

    return single_open(file,procfile_show,NULL);

}

static struct proc_ops proc_fops = {

    .proc_lseek = seq_lseek,

    .proc_open = procfile_open,

    .proc_read = procfile_read,

    .proc_release = single_release,

    .proc_write = procfile_write,

};

 

/\*\*

\*This function is called from main.c when the module is loaded

\*/

int __init my_proc_init(void)

{

    parent = proc_mkdir(PROCFS_NAME,NULL);

    entry = proc_create("system",0777,parent,&proc_fops);

    if(!entry)

        return -1;

    return 0;

}

/\*\*

\*This function is called from main.c when the module is unloaded

\*/

void __exit my_proc_cleanup(void)

{

    remove_proc_entry("system", parent);

    remove_proc_entry(PROCFS_NAME, NULL);

}
  

And we need a .h file to include the function prototypes.

   
  

/\* Just the function prototypes for now \*/

int __init my_proc_init(void);

void __exit my_proc_cleanup(void);
  

The Makefile also needs to be adjusted to work with our two files instead of just the one. Notice the differences in the top part of the file.

   
  

ifneq ($(KERNELRELEASE),)

\# kbuild part of makefile

obj-m := hello.o

hello-y := main.o proc.o

else

\# Run this Makefile as follows:

\# make clean && make all && make test

\#

KDIR= /lib/modules/$(shell uname -r)/build

all:

    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) M=$(PWD) modules

test:

    scp ./hello.ko dev@test-instance:~/

\-ssh test-instance sudo rmmod hello

    ssh test-instance sudo insmod ./hello.ko

    ssh test-instance cat /proc/hello/system

clean:

    rm -f *~

    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) M=$(PWD) clean

endif
  

And that should be it, compile and test and see how it works. Once you have the module loaded, you should be able to `cat /proc/hello/system`, `echo “this is a test” > /proc/hello/system` then `cat /proc/hello/system` again and observe the changes.

   
  

rob@rob-Ghost:~/Dev/hello$ sudo insmod ./hello.ko

rob@rob-Ghost:~/Dev/hello$ cat /proc/hello/system

rob@rob-Ghost:~/Dev/hello$ echo "Hello, world!" > /proc/hello/system

rob@rob-Ghost:~/Dev/hello$ cat /proc/hello/system

Hello, world!

rob@rob-Ghost:~/Dev/hello$ sudo rmmod hello

rob@rob-Ghost:~/Dev/hello$
  

Categories: Development, Training

By Rob Hartzenberg

September 17, 2020

Rob Hartzenberg
Author: Rob Hartzenberg

Linux Engineer

PREVIOUS

Strategies to lead remote team members WELL

NEXT

Kernel Module Snippets – Part 4 – UDP in the kernel