Creating Kobject Directory in /sys

And if you want to add your own module to the mix, you gotta create a kobject directory for it.

Now, before we dive into the details of creating a kobject directory, let me first explain what a kobject is and why it’s so ***** important in sysfs. A kobject is essentially an object that represents something within the kernel like a device or a driver. And by creating a kobject directory for your module, you can expose this object to user space via sysfs.

So how do we create one of these magical directories? Well, first things first: open up your favorite text editor and let’s get started on our code. We’ll be using C here because it’s the language most commonly used for kernel modules (and also because I don’t know any other languages).

First, we need to include some necessary headers that will allow us to interact with sysfs:

// This script includes necessary headers for interacting with sysfs in a kernel module.

// The <linux/module.h> header provides functions and macros for managing kernel modules.
#include <linux/module.h>

// The <linux/kernel.h> header provides functions and macros for interacting with the kernel.
#include <linux/kernel.h>

// The <linux/init.h> header provides functions and macros for initializing kernel modules.
#include <linux/init.h>

// The <linux/sysfs.h> header provides functions and macros for interacting with sysfs.
#include <linux/sysfs.h>

// The <linux/kobject.h> header provides functions and macros for creating and managing kernel objects.
#include <linux/kobject.h>

Next, we’ll create a struct to hold our kobject:

// This line creates a static struct variable named "my_kobj" to hold a kobject.
// The "static" keyword means that the variable can only be accessed within this file.

struct kobject {
    // This is the definition of the kobject struct.
    // It contains various fields and functions related to the kobject.
};

// This line initializes the "my_kobj" variable with a null value.
// This is important to prevent any potential errors when using the variable.

my_kobj = NULL;

// This line creates a pointer to a kobject and assigns it to the "my_kobj" variable.
// This pointer will be used to access and manipulate the kobject.

struct kobject *my_kobj_ptr = &my_kobj;

// This line checks if the "my_kobj" variable is not equal to NULL.
// If it is not equal to NULL, it means that the kobject has already been created.

if (my_kobj != NULL) {
    // If the kobject already exists, an error message is printed and the program exits.
    printf("Error: kobject already exists.\n");
    exit(1);
}

// This line creates a new kobject using the "kobject_create_and_add" function.
// The first argument is the name of the kobject, which is "my_kobj" in this case.
// The second argument is the parent kobject, which is set to NULL.
// The third argument is the path for the kobject, which is set to "/sys/kernel/".
// The last argument is the pointer to the kobject, which is "my_kobj_ptr".

my_kobj = kobject_create_and_add("my_kobj", NULL, "/sys/kernel/", my_kobj_ptr);

// This line checks if the kobject creation was successful.
// If it was not successful, an error message is printed and the program exits.

if (my_kobj == NULL) {
    printf("Error: kobject creation failed.\n");
    exit(1);
}

// This line creates a new attribute for the kobject using the "sysfs_create_file" function.
// The first argument is the kobject, which is "my_kobj".
// The second argument is the attribute name, which is "my_attr".
// The third argument is the pointer to the attribute, which is "my_attr_ptr".

my_attr = sysfs_create_file(my_kobj, "my_attr", &my_attr_ptr);

// This line checks if the attribute creation was successful.
// If it was not successful, an error message is printed and the program exits.

if (my_attr != 0) {
    printf("Error: attribute creation failed.\n");
    exit(1);
}

// This line prints a success message if all the previous steps were completed successfully.

printf("Kobject and attribute created successfully.\n");

Now let’s define the attributes that will be exposed in sysfs for this module. In this case, we’re going to expose an integer variable called “foo”:

// This script defines the attributes that will be exposed in sysfs for a module. 
// In this case, an integer variable called "foo" will be exposed.

// Declare and initialize the integer variable "foo" with a value of 0.
int foo = 0;

// Define a function to show the value of "foo" in the sysfs.
// Parameters:
// - kobj: pointer to the kobject structure
// - attr: pointer to the attribute structure
// - buf: pointer to the buffer where the value will be stored
// Returns:
// - the number of characters written to the buffer
static ssize_t show_foo(struct kobject *kobj, struct attribute *attr, char *buf) {
    // Use the sprintf function to format the value of "foo" as a string and store it in the buffer.
    // The "\n" adds a new line at the end of the string.
    return sprintf(buf, "%d\n", foo);
}

// Define a function to store a new value for "foo" in the sysfs.
// Parameters:
// - kobj: pointer to the kobject structure
// - attr: pointer to the attribute structure
// - buf: pointer to the buffer containing the new value
// - count: the number of characters in the buffer
// Returns:
// - the number of characters written to the buffer
static ssize_t store_foo(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) {
    // Declare a variable to store the new value for "foo".
    int val;
    // Use the strict_strtol function to convert the string in the buffer to an integer and store it in "val".
    // The base 10 is specified as the conversion base.
    // If the conversion fails, return an error.
    if (strict_strtol(buf, 10, &val))
        return -EINVAL;
    // Update the value of "foo" with the new value.
    foo = val;
    // Return the number of characters in the buffer.
    return count;
}

We’ve defined two functions here: `show_foo()` and `store_foo()`. The former will be used to read the value of “foo”, while the latter will allow us to write a new value. Note that we’re using strict_strtol() instead of sscanf() because it’s safer (and more reliable) when dealing with user input.

Now let’s create an attribute for our kobject:

// This line declares a static struct named "my_attr" with the attribute type.
static struct attribute my_attr = __ATTR("foo", 0644, show_foo, store_foo);

// The __ATTR macro is used to create an attribute for our kobject.
// "foo" is the name of the attribute, 0644 is the permission mode, and show_foo and store_foo are the functions used to read and write the attribute respectively.

// This line declares the attribute name as "foo".
__ATTR("foo", 0644, show_foo, store_foo);

// This line sets the permission mode for the attribute to 0644, which allows read and write access for the owner and read access for others.
__ATTR("foo", 0644, show_foo, store_foo);

// This line specifies the function used to read the attribute value.
__ATTR("foo", 0644, show_foo, store_foo);

// This line specifies the function used to write a new value for the attribute.
__ATTR("foo", 0644, show_foo, store_foo);

We’re using the `__ATTR()` macro to define this attribute. The first argument is the name of the attribute (in this case “foo”), and the remaining arguments are used to specify its permissions (read/write) and the functions that will be called when reading or writing it.

Finally, let’s create our kobject:

// This function is used to initialize the module and is called when the module is loaded.
static int __init my_module_init(void) {
    int error;
    pr_info("Loading module...\n"); // Prints a message to the kernel log.
    my_kobj = kobject_create_and_add("my_module", kernel_kobj); // Creates and adds a kobject named "my_module" to the kernel kobject.
    if (!my_kobj) {
        pr_err("Failed to create kobject.\n"); // Prints an error message to the kernel log if the kobject creation fails.
        error = -ENOMEM; // Sets the error code to indicate an out of memory error.
        goto fail; // Jumps to the "fail" label to handle the error.
    }
    error = sysfs_create_file(my_kobj, &my_attr); // Creates a sysfs file for the kobject using the "my_attr" attribute.
    if (error) {
        pr_err("Failed to create attribute file.\n"); // Prints an error message to the kernel log if the sysfs file creation fails.
        kobject_put(my_kobj); // Releases the reference to the kobject.
        my_kobj = NULL; // Sets the kobject pointer to NULL.
        goto fail; // Jumps to the "fail" label to handle the error.
    }
    return 0; // Returns 0 to indicate success.
fail:
    kobject_del(my_kobj); // Deletes the kobject.
    return error; // Returns the error code.
}

Here we’re using the `kobject_create_and_add()` function to create our kobject and add it to sysfs. We’re also creating a file for our attribute (using `sysfs_create_file()`) and returning an error if anything goes wrong.

And that’s it! Your module is now ready to be loaded into the kernel, and you should see your new kobject directory in /sys/kernel/my_module. You can read or write the value of “foo” by accessing this directory (either via a shell script or from within another program).

Just remember to be careful when dealing with user input, and always test your code thoroughly before loading it into the kernel.

SICORPS