How to use sysfs inside kernel module?
There is just no way to implement it properly. If you want to do it anyway, and also
understand the reason why it is a Bad Idea (tm), see this article
How do I make my kernel module's sysfs entry be owned by a non-root user?
Owner's and group's identifiers of a sysfs attribute are determined by the the kobject for which this attribute is created.
Specifically, it is get_ownership
field of the struct kobj_type
which may contain a callback for set owner's and group's identifiers. The field has following signature:
struct kobj_type {
...
void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};
When the callback is called, both *uid
and *gid
contain 0 (which corresponds to the root user). The callback may set these output parameters to other values.
How to attach file operations to sysfs attribute in platform driver?
It boils down to next:
- reuse existing kobject from
struct device
(from yourstruct platform_device
) forsysfs_create_group()
(instead of creating your ownkobject
) - use
DEVICE_ATTR()
to declarestruct device_attribute
instead of regular__ATTR()
, which createsstruct kobj_attribute
.
Here is how I created sysfs attributes for my platform driver.
Create structure you'll be using as private data in
show()
/store()
operations for your sysfs attribute (file). For example:struct mydrv {
struct device *dev;
long myparam;
};Allocate this structure in your driver's
probe()
:static int mydrv_probe(struct platform_device *pdev)
{
struct mydrv *mydrv;
mydrv = devm_kzalloc(&pdev->dev, sizeof(*mydrv), GFP_KERNEL);
mydrv->dev = &pdev->dev;
platform_set_drvdata(pdev, mydrv);
...
}Create
show()
/store()
functions:static ssize_t mydrv_myparam_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mydrv *mydrv = dev_get_drvdata(dev);
int len;
len = sprintf(buf, "%d\n", mydrv->myparam);
if (len <= 0)
dev_err(dev, "mydrv: Invalid sprintf len: %d\n", len);
return len;
}
static ssize_t mydrv_myparam_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct mydrv *mydrv = dev_get_drvdata(dev);
kstrtol(buf, 10, &mydrv->myparam);
return count;
}Create device attribute for those functions (right after those functions):
static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show,
mydrv_myparam_store);Declare attributes table (listing in fact sysfs files for you driver):
static struct attribute *mydrv_attrs[] = {
&dev_attr_myparam.attr,
NULL
};Declare attribute group (specifying in fact sysfs directory for your driver):
static struct attribute_group mydrv_group = {
.name = "mydrv",
.attrs = mydrv_attrs,
};
static struct attribute_group *mydrv_groups[] = {
&mydrv_group,
NULL
}which can be actually replaced with one line:
ATTRIBUTE_GROUPS(mydrv);
Create sysfs directory and files in your driver's
probe()
function:static int mydrv_probe(struct platform_device *pdev)
{
int ret;
...
ret = sysfs_create_group(&pdev->dev.kobj, &mydrv_group);
if (ret) {
dev_err(&pdev->dev, "sysfs creation failed\n");
return ret;
}
...
}Remove your sysfs files in your driver's
remove()
function:static int mydrv_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &mydrv_group);
...
}
Race condition note
As @FranzForstmayr correctly pointed out, there may be race condition when adding sysfs files with sysfs_create_group()
in mydrv_probe()
. That's because user-space can be already notified that those files exist before mydrv_probe()
called (where those files are actually being created by sysfs_create_group()
function). This issue covered in details in "How to Create a sysfs File Correctly" article by Greg Kroah-Hartman.
So in our case of platform_device
, instead of calling sysfs_create_group()
(and its counterpart sysfs_remove_group()
), you can use default attribute group. To do so, you need to assign corresponding .groups
field of your struct device
to your attribute groups variable:
static int mydrv_probe(struct platform_device *pdev)
{
...
pdev->dev.groups = mydrv_groups;
...
}
DISCLAIMER: I didn't test this code, though it should work, because of this code.
See [1,2,3] links for more insights on mentioned race condition.
For more examples, run next command in kernel source directory:
$ git grep -l --all-match -e platform_device -e attribute -e '\.groups =' -- drivers/
Also you can search by "default attribute" in commit messages:
$ git log --no-merges --oneline --grep="default attribute" -- drivers/
Some commits I found this way: [4,5,6,7].
References
[1] My attributes are way too racy, what should I do?
[2] PATCH: sysfs: add devm_sysfs_create_group() and friends
[3] [GIT PATCH] Driver core patches for 3.11-rc2
[4] commit 1
[5] commit 2
[6] commit 3
[7] commit 4
How to use the function from a custom kernel module?
Use sysfs
Checkout the list of various Linux kernel <--> Userspace interfaces.
To allow userspace to interact with a loadable kernel module, consider using sysfs.
To add support for sysfs within your loadable module, checkout the basics of a sys-fs entry.
A good guide with the best practices of creating sysfs entries should get you started the right way.
The userspace test will then change from
int retval = getpeuid(pid, uid);
to something that uses open
, write()
and read()
to interact with the sysfs entry just like a regular file.
( Why file? because everything is a file on UNIX. )
You could further simplify this to using a shell-script that uses echo
/cat
commands to pass/gather data from the loadable kernel module via the sysfs entry.
Alternate option : A beautiful/ugly Hack
Disclaimer: I agree that trying to use syscalls within a loadable kernel module is neither a proper solution, nor guaranteed to always work. I know what i am doing.
(Hover the mouse over the following block, ONLY if you agree to the above)
Checkout this answer and related code that describes a potential "hack" to allow implementing custom syscalls in loadable modules in any unused locations within the current syscall table of the kernel.
Also carefully go through the several answers/comments to this question. They deal with overcoming the problem of not being able to modify the syscall table. One of the comments also emphasises the fact that hypervisors implementing their own extensions are not likely to be affected by this "exploit" as they offer better protection of the syscall table.
Note that such non-standard interfaces may not always work and even if they do, they can stop working anytime. Stick to standard interfaces for reliability.
Related Topics
Arm-Linux-Gnueabi Compiler Options
Mono and Unmanaged Code in Ubuntu
Why 'Read' Command in Shell Script Is Missing Initial Characters
Why Isn't Git Bash Transforming The Path to *Nix Notation for My Python Installation
How to Send Email from The Command Line
Ubuntu "E: Unable to Locate Package MySQL "
How to Read N-Th Line from a Text File in Bash
Can't Install The Caret Package in R (In My Linux Machine)
What Is The Linux Command Line to Check Kernel Space and User Space Memory Used
Which Os/Platforms Implement Wait Morphing Optimization
Can't Use Gpg-Agent as an Ssh Agent
Extract Parent Domain Name from a List of Url Through Bash Shellscripting
Linux Kernel Hardware Break Points
Execute External Program with Trigger in Postgres 9.4
Linux Configuration - Ssmtp: Cannot Open Smtp.Gmail.Com:587
When Did Hup Stop Getting Sent and What How to Do About It