Implementing bcc's killsnoop in C - c

I'm trying to implement the killsnoop.py program in bcc in C. When executing the program, I'm getting a failed to load: -13 error. Can someone help me to debug this?
Note: For compilation, I've taken the libbpf-bootstrap example from Andrii Nakryiko's blog post.
Error message:
Below is the program that I've used.
killsnoop.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "killsnoop.h"
struct val_t {
__u32 uid;
__u32 pid;
int sig;
int tpid;
char comm[TASK_COMM_LEN];
};
struct data_t {
__u32 uid;
__u32 pid;
int tpid;
int sig;
int ret;
char comm[TASK_COMM_LEN];
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, struct val_t);
__uint(max_entries, 10240);
} info_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, struct data_t);
__uint(max_entries, 10240);
} event SEC(".maps");
SEC("kprobe/__x64_sys_kill")
int entry_probe(struct pt_regs *ctx, int tpid, int sig) {
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
__u32 tid = (__u32) pid_tgid;
__u64 uid_gid = bpf_get_current_uid_gid();
__u32 uid = (__u32) uid_gid;
struct val_t val = {
.uid = uid,
.pid = pid
};
if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) {
val.tpid = tpid;
val.sig = sig;
bpf_map_update_elem(&info_map, &tid, &val, BPF_ANY);
}
return 0;
}
SEC("kretprobe/__x64_sys_kill")
int return_probe(struct pt_regs *ctx) {
struct data_t data = {};
struct val_t *valp;
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
__u32 tid = (__u32) pid_tgid;
valp = bpf_map_lookup_elem(&info_map, &tid);
if (!valp) {
return 0; // missed entry
}
bpf_core_read(&data.comm, sizeof(data.comm), valp->comm);
data.pid = pid;
data.tpid = valp->tpid;
data.ret = PT_REGS_RC_CORE(ctx);
data.sig = valp->sig;
data.uid = valp->uid;
bpf_perf_event_output(ctx, &event, BPF_F_CURRENT_CPU, &data, sizeof(data));
bpf_map_delete_elem(&info_map, &tid);
return 0;
}
killsnoop.c
#include <signal.h>
#include "killsnoop.skel.h"
#include "killsnoop.h"
#define OUTPUT_FORMAT "%lld %d %d %s %d %d"
#define PERF_POLL_TIMEOUT_MS 10
#define PERF_BUFFER_PAGES 64
static volatile int shutdown = 0;
static void sig_int(int signal) {
shutdown = 1;
}
void handle_event(void *ctx, int cpu, void *data, u32 data_size) {
const struct event *e = data;
time_t curr_time;
time(&curr_time);
printf(OUTPUT_FORMAT, (long long) curr_time, e->pid, e->tpid, e->comm, e->sig, e->uid);
}
void handle_lost_event(void *ctx, int cpu, u64 lost_cnt) {
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
}
int main(int argc, char **argv) {
int err;
struct perf_buffer *perfBuffer;
struct killsnoop_bpf *obj;
obj = killsnoop_bpf__open_and_load();
if (!obj) {
fprintf(stderr, "failed to open/load BPF skeleton!");
goto cleanup;
}
err = killsnoop_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}
perfBuffer = perf_buffer__new(bpf_map__fd(obj->maps.info_map), PERF_BUFFER_PAGES, handle_event, handle_lost_event,
NULL, NULL);
if (!perfBuffer) {
err = -errno;
fprintf(stderr, "failed to open perf buffer: %d\n", err);
goto cleanup;
}
if (signal(SIGINT, sig_int) == SIG_ERR) {
fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
err = 1;
goto cleanup;
}
while (!shutdown) {
err = perf_buffer__poll(perfBuffer, PERF_POLL_TIMEOUT_MS);
if (err < 0 && err != -EINTR) {
fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
goto cleanup;
}
/* reset err to return 0 if exiting */
err = 0;
}
cleanup:
perf_buffer__free(perfBuffer);
killsnoop_bpf__destroy(obj);
return err != 0;
}
killsnoop.h
#define TASK_COMM_LEN 16
typedef unsigned int u32;
typedef unsigned long long u64;
struct event {
u32 uid;
u32 pid;
int tpid;
int sig;
int ret;
char comm[TASK_COMM_LEN];
};

BCC tends to do a little bit of background magic which allows you to use the arguments of the attachment point directly in your eBPF program like you are doing:
entry_probe(struct pt_regs *ctx, int tpid, int sig)
However, this is BCC specific. With libbpf you have to use the BPF_KPROBE_SYSCALL macro to get similar behavior. For example: https://elixir.bootlin.com/linux/v5.18.14/source/tools/testing/selftests/bpf/progs/bpf_syscall_macro.c#L68
In your case:
SEC("kprobe/" SYS_PREFIX "sys_kill")
int BPF_KPROBE_SYSCALL(entry_probe, int tpid, int sig) {
...
The reason you are getting this error is because all arguments are saves to the stack once the BPF program is entered. Thus the program attempts to write R3 which is int sig in this case to the stack. But R3 is not defined, only R1 is which is the ctx. And you are not allowed to read from uninitialized registers hence the R3 !read_ok error.

Related

passing floating point by casting to int and print in ioctl implementation

so I have this code
this is what I am doing in ioctl implementation
if( copy_from_user(&value ,(struct aa*) arg, sizeof(value)) )
{
pr_err("Data Write : Err!\n");
}
__u64 a=value.a;
__u32 *b=(__u32 *)a;
pr_info("wow Value = [%x]\n", (int)b[0]);
but I am passing from userspace float so but my passed values are not correctly printing in printk
this is my program
struct aa
{
uint64_t a;
};
#define WR_VALUE _IOW('a','a',struct aa*)
#define RD_VALUE _IOR('a','b',struct aa*)
int main()
{
struct aa a;
float *f=(float[]){2,2,3};
a.a=(uint64_t)f;
printf("sizeof = %zu\n",sizeof(*f));
int fd;
int32_t value, number;
printf("*********************************\n");
printf("*******WWW.EmbeTronicX.com*******\n");
printf("\nOpening Driver\n");
fd = open("/dev/etx_device", O_RDWR);
if(fd < 0) {
printf("Cannot open device file...\n");
return 0;
}
printf("Enter the Value to send\n");
scanf("%d",&number);
printf("Writing Value to Driver\n");
ioctl(fd, WR_VALUE, (struct aa *) &a);
printf("Reading Value from Driver\n");
ioctl(fd, RD_VALUE, (struct aa*) &a);
printf("Value is %d\n", value);
printf("Closing Driver\n");
close(fd);
}
full code
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h> //kmalloc()
#include<linux/uaccess.h> //copy_to/from_user()
#include <linux/ioctl.h>
#include <asm/fpu/api.h>
struct aa
{
__u64 a;
};
#define WR_VALUE _IOW('a','a',struct aa *)
#define RD_VALUE _IOR('a','b',struct aa *)
struct aa value;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
/*
** Function Prototypes
*/
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/*
** File operation sturcture
*/
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = etx_open,
.unlocked_ioctl = etx_ioctl,
.release = etx_release,
};
/*
** This function will be called when we open the Device file
*/
static int etx_open(struct inode *inode, struct file *file)
{
return 0;
}
/*
** This function will be called when we close the Device file
*/
static int etx_release(struct inode *inode, struct file *file)
{
return 0;
}
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case WR_VALUE:
if( copy_from_user(&value ,(struct aa*) arg, sizeof(value)) )
{
pr_err("Data Write : Err!\n");
}
__u64 a=value.a;
__u32 *b=(__u32 *)a;
pr_info("wow Value = [%x]\n", (int)b[0]);
kernel_fpu_end();
break;
case RD_VALUE:
if( copy_to_user((struct aa*) arg, &value, sizeof(value)) )
{
pr_err("Data Read : Err!\n");
}
break;
default:
pr_info("Default\n");
break;
}
return 0;
}
/*
** Module Init function
*/
static int __init etx_driver_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
pr_err("Cannot allocate major number\n");
return -1;
}
pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev,&fops);
/*Adding character device to the system*/
if((cdev_add(&etx_cdev,dev,1)) < 0){
pr_err("Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
pr_err("Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
pr_err("Cannot create the Device 1\n");
goto r_device;
}
pr_info("Device Driver Insert...Done!!!\n");
return 0;
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
return -1;
}
/*
** Module exit function
*/
static void __exit etx_driver_exit(void)
{
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
pr_info("Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx#gmail.com>");
MODULE_DESCRIPTION("Simple Linux device driver (IOCTL)");
MODULE_VERSION("1.5");
what am I doing wrong in kernel ioctl because I casted float to uint_64 like a.a=(uint64_t)f; and passed struct a to kernel now I want to read float *f elements passed from userspace through ioctl in ioctl implementation

Using perf_event_open() to get sample address ,however the addr=0

I tried to use perf_event_open() to track all the store instructions to get their access address. I found only when I set attr.precise_ip > 0, I can get the non-zero address. But when I ran the same process on vm instead of host, the error massage was "Operation not supported", I can fix this problem by setting precise_ip = 0 on vm, but now I only get bunch of addresses equal to zero. I don't understand why precise_ip is related to the sample addrress which is not pointed out on document, and I also don't understand why I can't set precise_ip = 1 on vm while I can do it on host. Is there anybody can help me??
FYI: I use - cpu host option when I start vm using qemu-system-x86_64
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#define PERF_PAGES (1 + (1 << 16))
struct perf_sample {
struct perf_event_header header;
__u64 ip;
__u32 pid, tid; /* if PERF_SAMPLE_TID */
__u64 addr; /* if PERF_SAMPLE_ADDR */
__u64 weight; /* if PERF_SAMPLE_WEIGHT */
/* __u64 data_src; /\* if PERF_SAMPLE_DATA_SRC *\/ */
__u64 phy_addr;
};
int perf_event_open(struct perf_event_attr *attr,pid_t pid,int cpu,int group_fd,unsigned long flags)
{
return syscall(__NR_perf_event_open,attr,pid,cpu,group_fd,flags);
}
void workload()
{
int i,c=0;
for(i=0;i<100000000;i++)
{
c+=i*i;
c-=i*100;
c+=i*i*i/100;
}
}
int startup()
{
struct perf_event_attr attr;
memset(&attr,0,sizeof(struct perf_event_attr));
attr.type = PERF_TYPE_RAW;
attr.size = sizeof(struct perf_event_attr);
attr.config = 0x82d0;
attr.config1 = 0;
attr.sample_period = 1000;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_ADDR | PERF_SAMPLE_PHYS_ADDR ;
attr.disabled = 0;
//attr.inherit = 1;
attr.exclude_kernel = 1;
attr.exclude_hv = 1;
attr.exclude_callchain_kernel = 1;
attr.exclude_callchain_user = 1;
attr.precise_ip = 1; // when i set attr.precise_ip = 0 , all the addr = 0;
int fd=perf_event_open(&attr,0,-1,-1,0);
if(fd<0)
{
perror("Cannot open perf fd!");
return -1;
}
return fd;
}
void scan_thread(struct perf_event_mmap_page *p)
{
char *pbuf = (char *)p + p->data_offset;
__sync_synchronize();
printf("%d,\n", p->data_size);
if(p->data_head == p->data_tail) {
return;
}
struct perf_event_header *ph = (void *)(pbuf + (p->data_tail % p->data_size));
struct perf_sample* ps;
switch(ph->type) {
case PERF_RECORD_SAMPLE:
ps = (struct perf_sample*)ph;
// assert(ps != NULL);
if(ps == NULL)
{
printf("null\n");
}
if(ps!= NULL && ps->addr != 0) {
printf("ip %lx\n", ps->ip);
printf("tid %d\n", ps->tid);
printf("addr: %lx \n", ps->addr);
}
//printf("addr, %lx\n", ps->addr);
//printf("phy addr, %lx\n", ps->phy_addr);
break;
default:
printf("type %d\n", ph->type);
break;
}
}
int main()
{
int fd = startup();
size_t mmap_size = sysconf(_SC_PAGESIZE) * PERF_PAGES;
struct perf_event_mmap_page *p = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// start to perf
ioctl(fd,PERF_EVENT_IOC_ENABLE,0);
int a= 0;
while(1)
{
// uint64_t instructions;
// read(fd,&instructions,sizeof(instructions));
// printf("instructions=%ld\n",instructions);
// sleep(1);
workload();
scan_thread(p);
sleep(1);
}
}

How to get struct i2c_client *client structure inside kernel thread

I am now developing a kernel module to handle two digits 7-segment led indicator via I2C device on RaspberryPi4 with RaspberryPiOS.
This module uses kernel thread to handle 7-segment led display, change digit position.
Sending command to i2c device requires i2c_client* structure, it can be obtained via formal argument of probe function.
Kernel thread does not have formal argument of i2c_client structure.
My solution is to store a pointer of i2c_client* structure into a global variable and use this pointer inside kernel thread, and call i2c function with this pointer.
My kernel module works well, at least now...
Do you have better solution to use i2c function in kernel thread? or exists better solution?
My entire code is below...
//i2c_7seg2_udev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/kthread.h>
MODULE_LICENSE("GPL v2");
#define DEVICE_NAME "i2c_7seg2_udev"
#define MINOR_BASE 0
#define MINOR_NUM 1
static unsigned int major_num;
struct cdev i2c_7seg2_udev_cdev;
void init_i2c_gpio(void);
void turn_off_7seg(void);
void turn_off_7seg(void);
void set_7seg(unsigned int number);
void change_7seg_keta(void);
#define LOOP_SLEEP_US (5000)
static struct task_struct *kthread;
static int dynamic_7seg_kthread(void *data);
static void init_kthread(void)
;
static int dynamic_7seg_kthread(void *data)
{
while(!kthread_should_stop()){
change_7seg_keta();
usleep_range(LOOP_SLEEP_US, LOOP_SLEEP_US * 2);
}
return 0;
}
static void init_kthread(void)
{
kthread = kthread_create(dynamic_7seg_kthread, NULL, "dynamic_7seg_kthread");
wake_up_process(kthread);
}
static int init_regist_device(void);
static struct i2c_client *i2c_client_data;
#define DRIVER_NAME "i2c_7seg2_udev"
static struct i2c_device_id i2c_7seg2_udev_device_idtable[] = {
{"I2C_7SEG2_UDEV", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, i2c_7seg2_udev_device_idtable);
static int i2c_7seg2_udev_device_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
if(init_regist_device() < 0){
printk(KERN_DEBUG "i2c_7seg2_udev: initialize failed.\n");
return -1;
}
printk(KERN_DEBUG "i2c_7seg2_udev_device connected.\n");
printk(KERN_DEBUG "id.name = %s, id.driver_data = %ld", id->name, id->driver_data);
printk(KERN_DEBUG "device address is: 0x%02X\n", client->addr);
i2c_client_data = client;
init_i2c_gpio();
set_7seg(12);
init_kthread();
return 0;
}
static int i2c_7seg2_udev_device_remove(struct i2c_client *client)
{
dev_t dev;
turn_off_7seg();
printk(KERN_DEBUG "i2c_7seg2_udev: wait for thread to be terminated.\n");
kthread_stop(kthread);
printk(KERN_DEBUG "i2c_7seg2_udev: thread terminated.\n");
dev = MKDEV(major_num, MINOR_BASE);
cdev_del(&i2c_7seg2_udev_cdev);
unregister_chrdev_region(dev, MINOR_NUM);
printk(KERN_DEBUG "i2c_7seg2_udev_device disconnected.\n");
return 0;
}
static struct i2c_driver i2c_7seg2_udev_device_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.id_table = i2c_7seg2_udev_device_idtable,
.probe = i2c_7seg2_udev_device_probe,
.remove = i2c_7seg2_udev_device_remove,
};
#define MAX_7SEG_KETA (2)
//NOTE: "KETA" means digit position, in Japanese.
static const unsigned int seg7_pattern[] =
//--gfedcba
{0b00111111, //0
0b00000110, //1
0b01011011, //2
0b01001111, //3
0b01100110, //4
0b01101101, //5
0b01111100, //6
0b00100111, //7
0b01111111, //8
0b01100111 //9
};
unsigned char value_7seg[MAX_7SEG_KETA];
DEFINE_MUTEX(__mutex_value_7seg);
void set_7seg(unsigned int number){
unsigned int i;
printk(KERN_DEBUG "i2c_7seg2_udev: value %d .\n", number);
for( i = 0; i < MAX_7SEG_KETA; i++){
value_7seg[MAX_7SEG_KETA - i - 1] = (number % 10) + '0';
number = number / 10;
}
}
void change_7seg_keta(void)
{
static unsigned int keta = 0;
unsigned char keta_shift;
unsigned int number;
keta_shift = 0x01 << keta;
i2c_smbus_write_byte_data(i2c_client_data, 0x03, ~keta_shift); //P1に出力
number = value_7seg[MAX_7SEG_KETA - keta - 1] - '0';
i2c_smbus_write_byte_data(i2c_client_data, 0x02, ~seg7_pattern[number]); //P0に出力
keta ++;
if( keta >= MAX_7SEG_KETA ){
keta = 0;
}
}
void turn_off_7seg(void){
i2c_smbus_write_byte_data(i2c_client_data, 0x02, 0x7F); //P0に出力
}
void init_i2c_gpio(void){
i2c_smbus_write_byte_data(i2c_client_data, 0x06, 0x00);
i2c_smbus_write_byte_data(i2c_client_data, 0x07, 0x00);
}
static int i2c_7seg2_udev_open(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "i2c_7seg2_udev: opened.\n");
return 0;
}
static int i2c_7seg2_udev_close(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "i2c_7seg2_udev: closed.\n");
return 0;
}
static ssize_t i2c_7seg2_udev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
unsigned char read_datas[MAX_7SEG_KETA + 2];
unsigned long bytes_left;
const unsigned long bytes_to_send = MAX_7SEG_KETA + 2;
unsigned int i;
for(i = 0; i < MAX_7SEG_KETA; i++ ){
read_datas[i] = value_7seg[i];
}
read_datas[i] = '\n';
read_datas[i + 1] = '\0';
bytes_left = copy_to_user(buf, read_datas, bytes_to_send);
return (bytes_to_send - bytes_left);
}
static ssize_t i2c_7seg2_udev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
unsigned char write_values[MAX_7SEG_KETA + 2];
unsigned long bytes_left;
const unsigned long bytes_to_send = MAX_7SEG_KETA + 2;
int i;
printk(KERN_DEBUG "i2c_7seg2_udev: write.");
printk(KERN_DEBUG "i2c_7seg2_udev: write.");
bytes_left = copy_from_user(write_values, buf, bytes_to_send);
mutex_lock(&__mutex_value_7seg);
for(i = 0; i < MAX_7SEG_KETA; i++){
if( (write_values[i] >= '0') && (write_values[i] <= '9') ){
value_7seg[i] = write_values[i];
}
}
mutex_unlock(&__mutex_value_7seg);
return (bytes_to_send - bytes_left);
}
struct file_operations s_i2c_7seg2_udev_fops = {
.open = i2c_7seg2_udev_open,
.release = i2c_7seg2_udev_close,
.read = i2c_7seg2_udev_read,
.write = i2c_7seg2_udev_write,
};
int init_regist_device(void)
{
int device_num = 0;
int devnum_err = 0;
int cdev_err = 0;
dev_t dev;
devnum_err = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DEVICE_NAME);
if (devnum_err != 0) {
printk(KERN_ERR "devnum_err = %d\n", devnum_err);
return -1;
}
if (device_num != 0) {
printk(KERN_ERR "i2c_7seg2_udev: error_init_regist_device , %d\n", device_num);
return -1;
}
major_num = MAJOR(dev);
cdev_init(&i2c_7seg2_udev_cdev, &s_i2c_7seg2_udev_fops);
i2c_7seg2_udev_cdev.owner = THIS_MODULE;
cdev_err = cdev_add(&i2c_7seg2_udev_cdev, dev, MINOR_NUM);
if (cdev_err != 0) {
printk(KERN_ERR "cdev_add = %d\n", cdev_err);
unregister_chrdev_region(dev, MINOR_NUM);
return -1;
}
printk(KERN_DEBUG "i2c_7seg2_udev: device registerd.\n");
return 0;
}
static int i2c_7seg2_udev_device_init(void)
{
printk(KERN_DEBUG "i2c_7seg2_udev device driver loaded.\n");
i2c_add_driver(&i2c_7seg2_udev_device_driver);
return 0;
}
static void i2c_7seg2_udev_device_exit(void)
{
printk(KERN_DEBUG "i2c_7seg2_udev device driver unloading.\n");
i2c_del_driver(&i2c_7seg2_udev_device_driver);
}
module_init(i2c_7seg2_udev_device_init);
module_exit(i2c_7seg2_udev_device_exit);
The second argument of the function kthread_create() is void *data.
Right now you are passing NULL, you can pass your pointer to the struct i2c_client instead.
Then you can do:
static int dynamic_7seg_kthread(void *data) {
struct i2c_client *client = data;
while (!kthread_should_stop()) {
change_7seg_keta(client);
usleep_range(LOOP_SLEEP_US, LOOP_SLEEP_US * 2);
}
return 0;
}
Thus eliminating global variable from the code.

IOCTL error while passing struct to driver

I am trying to pass a struct pwmData type variable to my driver through ioctl. But in my app I get an Invalid Argument error through perror.
Can someone please tell me why I am getting this?
My .h file:
typedef struct
{
unsigned int pwmChannel;
unsigned int pwmDutyCycle;
unsigned int pwmPeriod;
}pwmData;
#define PWM_MAGIC '0xF2' //Is not used in kernel, hence used here
#define ENABLE_PWM _IOW(PWM_MAGIC, 1,pwmData*)
#define DISABLE_PWM _IOW(PWM_MAGIC, 2,pwmData*)
My app(where fd is my file descriptor):
pwmData param;
param.pwmPeriod = 1000;
param.pwmDutyCycle = 500;
param.pwmChannel = 1;
ret = ioctl(fd, ENABLE_PWM, &param); // Check casting
if(ret<0){
printf("ENABLE IOCTL failed %d.\n", ret);
perror("Error:");
exit(-1);
}
My driver's ioctl part:
static long pwm_device_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
printk(KERN_DEBUG "Entered IOCTL\n");
int retval;
static pwmData* data;
data = (void *)kmalloc(sizeof(pwmData)+1,GFP_KERNEL);
switch (cmd)
{
case ENABLE_PWM:
if(copy_from_user((void*)data, (void*)arg, sizeof(pwmData)))
{
return -EFAULT;
}
printk(KERN_DEBUG "%d %d %d\n",data->pwmChannel,data->pwmDutyCycle,data->pwmPeriod);
retval = pwm_config(&cpwm1->chip.pwms[(data->pwmChannel) - 1], data->pwmDutyCycle, data->pwmPeriod);
if(retval == 0)
{
retval = pwm_enable(&cpwm1->chip.pwms[(data->pwmChannel) - 1]);
return retval;
}
else
{
return retval;
}
break;
And similarly for PWM_DISABLE.
I have just started linux driver devel. so any suggestions would be apprectiated.

How to send and receive messages from function other than registered callback function in Netlink socket?

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
....
}

Resources