summaryrefslogtreecommitdiffstats
path: root/md/notes/kernel/create_sysfs_entry.md
blob: 529930adfee761959a7da5a8249fc44918725fd9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
title:Create sysfs entry
keywords:kernel,linux,threads

# Create sysfs entry

## Starting point

__Makefile__
```Makefile
obj-m += sysfs_entry.o

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

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean
```

__sysfs_entry.c__
```c
#include <linux/module.h>  /* Needed by all modules */
#include <linux/kernel.h>

int sysfs_entry_init( void )
{
	printk(KERN_DEBUG "Hello Sysfs!\n");
	return 0;
}

void sysfs_entry_exit( void )
{
	printk(KERN_DEBUG "Exit Sysfs!\n");
}

module_init( sysfs_entry_init );
module_exit( sysfs_entry_exit );

MODULE_LICENSE("GPL");
```

## Creating entries

### Kobject structure

```c
struct kobject {
	const char		*name;
	struct list_head	entry;
	struct kobject		*parent;
	struct kset		*kset;
	const struct kobj_type	*ktype;
	struct kernfs_node	*sd; /* sysfs directory entry */
	struct kref		kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
	struct delayed_work	release;
#endif
	unsigned int state_initialized:1;
	unsigned int state_in_sysfs:1;
	unsigned int state_add_uevent_sent:1;
	unsigned int state_remove_uevent_sent:1;
	unsigned int uevent_suppress:1;
};
```

### Add entry to /sys/kernel

__sysfs_entry.c__
```c
struct kobject *sysfs_kobj;

int sysfs_entry_init( void )
{
	...
	
	sysfs_kobj = kobject_create_and_add("secret", kernel_kobj);

	...
	return 0;
}

void sysfs_entry_exit( void )
{
	...

	kobject_put(sysfs_kobj);
}

```

When loading module with 
```sh
sudo insmod sysfs_entry.ko
```

There should be new directory under /sys/kernel called secret.

### Add file under the directory

__sysfs_entry.c__
```c

...
volatile int secret_value;

struct kobject *sysfs_kobj;
struct kobj_attribute secret_attr = __ATTR(secret_value, 0660, NULL, NULL);
...

int sysfs_entry_init( void )
{
	...
	
	if(sysfs_create_file(sysfs_kobj, &secret_attr.attr)) {
		pr_err("Cant create secret file");
	}


	...
	return 0;
}

void sysfs_entry_exit( void )
{
	...
	sysfs_remove_file(sysfs_kobj, &secret_attr.attr);
	...
	
}
```

Now we are able to find secret_value file and attempt to read values

```sh
sudo cat /sys/kernel/secret/secret_value 
cat: /sys/kernel/secret/secret_value: Input/output error
```

### Read write value from the syfs entry

__sysfs_entry.c__
```c
...
static ssize_t secret_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
	printk(KERN_DEBUG "Show value %d\n", secret_value);
	return sprintf(buf, "%d", secret_value);
}

static ssize_t secret_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) {
	printk(KERN_DEBUG "Set value %s %ldd\n", buf, count);
	sscanf(buf, "%d", &secret_value);
	return count;
}

struct kobj_attribute secret_attr = __ATTR(secret_value, 0660, secret_show, secret_store);
...

int sysfs_entry_init( void )
{
	...
	
	...
	return 0;
}

void sysfs_entry_exit( void )
{
	...
	sysfs_remove_file(sysfs_kobj, &secret_attr.attr);
	kobject_put(sysfs_kobj);
	...
}
```

Now we are able to set and read values from sys file


```sh
sudo cat /sys/kernel/secret/secret_value
```
or
```sh
echo 1 | sudo tee -a /sys/kernel/secret/secret_value
```

## List of functions used

| Function name | Note |
|---|---|
| kobject_create_and_add |  |
| sysfs_create_file |  |
| sysfs_remove_file |  |
| kobject_put |  |
|  |  |



## Final result

__sysfs_entry.c__
```c
#include <linux/module.h>  /* Needed by all modules */
#include <linux/kernel.h>

volatile int secret_value;

struct kobject *sysfs_kobj;

static ssize_t secret_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
	printk(KERN_DEBUG "Show value %d\n", secret_value);
	return sprintf(buf, "%d", secret_value);
}


static ssize_t secret_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) {
	printk(KERN_DEBUG "Set value %s %ldd\n", buf, count);
	sscanf(buf, "%d", &secret_value);
	return count;
}

struct kobj_attribute secret_attr = __ATTR(secret_value, 0660, secret_show, secret_store);


int sysfs_entry_init( void )
{
	printk(KERN_DEBUG "Hello Syscfs!\n");

	sysfs_kobj = kobject_create_and_add("secret", kernel_kobj);

	if(sysfs_create_file(sysfs_kobj, &secret_attr.attr)) {
		pr_err("Cant create secret file");
	}

	return 0;
}

void sysfs_entry_exit( void )
{
	printk(KERN_DEBUG "Exit Sysfs!\n");

	sysfs_remove_file(sysfs_kobj, &secret_attr.attr);
	kobject_put(sysfs_kobj);
}

module_init( sysfs_entry_init );
module_exit( sysfs_entry_exit );

MODULE_LICENSE("GPL");

```

## Links

https://man7.org/linux/man-pages/man5/sysfs.5.html  
https://embetronicx.com/tutorials/linux/device-drivers/sysfs-in-linux-kernel/  
https://elixir.bootlin.com/linux/v6.1.64/source/include/linux/sysfs.h  
https://docs.kernel.org/filesystems/sysfs.html  
https://elixir.bootlin.com/linux/v6.1.64/source/include/linux/kobject.h#L64  
https://pradheepshrinivasan.github.io/2015/07/02/Creating-an-simple-sysfs/