Custom PCI driver fails to probe device - c

After registering a PCI driver by calling pci_register_driver() within the init_module function, the driver is supposed to take control of any PCI devices which do not already have drivers, assuming that the devices match the vendor ID, device ID, etc. as specified in the struct pci_device_id.
I would like to force the Ethernet Controller to use my driver, just for the sake of experimentation (e.g. reading configuration bytes). On a Virtualbox guest (Mint, kernel 3.13.0), I blacklisted the Ethernet Controller's driver, ran update-initramfs -u, and rebooted. This successfully disassociated the default driver from the Controller, as the driver no longer showed up in the output of lspci -k.
However, when I loaded the module, a few devices that had previously been missing showed up in the output of lspci -k (with my driver controlling them), but the Ethernet Controller still lacked a "Kernel driver in use: " line. How can I make my module recognize and own the Controller?
Note that I used PCI_ANY_ID for the vendor and device fields, and left the other fields of the struct pci_device_id uninitialized. So I would expect the driver to probe any device which currently lacks a driver.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h>
MODULE_LICENSE("GPL");
int init_module(void);
void cleanup_module(void);
static int pci_probe(struct pci_dev *, const struct pci_device_id *);
static void pci_remove(struct pci_dev *dev);
#define SUCCESS 0
#define FAILURE -1
static struct pci_device_id my_pci_id = {
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID
};
static struct pci_driver my_pci_driver = {
.name = "kmod_driver",
.id_table = &my_pci_id,
.probe = pci_probe,
.remove = pci_remove
};
int init_module(void)
{
return pci_register_driver(&my_pci_driver);
}
void cleanup_module(void)
{
pci_unregister_driver(&my_pci_driver);
}
static int pci_probe(struct pci_dev *dev, const pci_device_id *id)
{
int enableStatus = pci_enable_device(dev);
.....
return SUCCESS;
}
static void pci_remove(struct pci_dev *dev)
{
return;
}

You also need to include the subvendor and subdevice fields (setting them to PCI_ANY_ID as well). The matching function is this:
static inline const struct pci_device_id *
pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
{
if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
(id->device == PCI_ANY_ID || id->device == dev->device) &&
(id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
(id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
!((id->class ^ dev->class) & id->class_mask))
return id;
return NULL;
}
You can use the PCI_DEVICE macro to supply the appropriate sub members:
static struct pci_device_id my_pci_id = {
PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID),
};

Related

'struct mm_struct' has no member named 'mmap_sem' error in the module build after switching from 5.4.21 to 5.10.0-rc5

I've been using this function in my linux device driver. This is to pin user pages for user virtual address (required number of pages) and get kernel address of it. By the ways, it was used in linux-5.4.21. (I think this method may have some problem. but it worked so I was using it)
static unsigned long uvirt_to_kvirt_ppin(unsigned long uvirt, unsigned long length, struct page *pages)
{
int res;
int offs;
uint64_t *kvpaddr;
uint64_t kvaddr;
uint64_t paddr;
offs = uvirt % PAGE_SIZE;
down_read(&current->mm->mmap_sem);
res = get_user_pages( uvirt, length>>PAGE_SHIFT, FOLL_WRITE, &pages, NULL);
if (res) {
kvpaddr = kmap(pages);
kvaddr = ((unsigned long long int)(kvpaddr)+offs);
paddr = page_to_phys(pages) + offs;
}
else {
printk("get_user_pages failed! res = %x\n", res);
return -1;
}
up_read(&current->mm->mmap_sem);
return kvaddr;
}
But today, when I was trying to build the module now against kernel 5.10.0-rc5, I met with this error message. This means as kernel was upgraded, the structure mm_struct was chagned.
/home/ckim/prj/abdsn/ab21sim/ab21tsim/QEMU/qemu_test/test_ldd_vanila/axpu_ldd_kc.c: In function ??uvirt_to_kvirt_ppin??:
/home/ckim/prj/abdsn/ab21sim/ab21tsim/QEMU/qemu_test/test_ldd_vanila/axpu_ldd_kc.c:246:26: error: ??struct mm_struct?? has no member named ??mmap_sem??; did you mean ??mmap_base???
246 | down_read(&current->mm->mmap_sem);
| ^~~~~~~~
| mmap_base
/home/ckim/prj/abdsn/ab21sim/ab21tsim/QEMU/qemu_test/test_ldd_vanila/axpu_ldd_kc.c:257:27: error: ??struct mm_struct?? has no member named ??mmap_sem??; did you mean ??mmap_base???
257 | up_read(&current->mm->mmap_sem);
| ^~~~~~~~
| mmap_base
How should I change it for 5.10.0-rc5? I searched the use of get_user_pages in 5.10.0-rc5 but there seems to be no locking around it. So, can I just remove the down_read and up_read lines?
Probably I'll just go with it.(removing the lines).
In Linux kernel 5.8, the mmap_sem member of struct mm_struct was renamed to mmap_lock and a new mmap locking API was added.
You could do something like this:
#include <linux/mm.h>
#ifndef MMAP_LOCK_INITIALIZER
/* Define mmap locking API for pre-5.8 kernels. */
/* This one should not be needed in a driver. */
static inline void mmap_init_lock(struct mm_struct *mm)
{
init_rwsem(&mm->mmap_sem);
}
static inline void mmap_write_lock(struct mm_struct *mm)
{
down_write(&mm->mmap_sem);
}
static inline int mmap_write_lock_killable(struct mm_struct *mm)
{
return down_write_killable(&mm->mmap_sem);
}
static inline bool mmap_write_trylock(struct mm_struct *mm)
{
return down_write_trylock(&mm->mmap_sem) != 0;
}
static inline void mmap_write_unlock(struct mm_struct *mm)
{
up_write(&mm->mmap_sem);
}
static inline void mmap_write_downgrade(struct mm_struct *mm)
{
downgrade_write(&mm->mmap_sem);
}
static inline void mmap_read_lock(struct mm_struct *mm)
{
down_read(&mm->mmap_sem);
}
static inline int mmap_read_lock_killable(struct mm_struct *mm)
{
return down_read_killable(&mm->mmap_sem);
}
static inline bool mmap_read_trylock(struct mm_struct *mm)
{
return down_read_trylock(&mm->mmap_sem) != 0;
}
static inline void mmap_read_unlock(struct mm_struct *mm)
{
up_read(&mm->mmap_sem);
}
#endif /* MMAP_LOCK_INITIALIZER */
Then replace down_read(&current->mm->mmap_sem); with mmap_read_lock(current->mm); and replace up_read(&current->mm->mmap_sem); with mmap_read_unlock(current->mm);.

struct usb_device_id does not contain USB device information

I am new to Linux kernel and I am presently learning USB device drivers.
I wrote a simple USB driver with probe callback and disconnect callback functions.
In the probe callback I try to display values of the struct usb_device_id argument passed to it. The problem is that structure variable shows product id and vendor id as 0.
What is the reason for this? Am I doing all right?
The code snippets from the driver are
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/usb.h>
int probe_callback(struct usb_interface *intf, const struct usb_device_id *id);
void disconnect_callback(struct usb_interface *intf);
struct usb_device_id usb_ids[]={
{.driver_info=42},
{}
};
struct usb_driver USB_DRIVER={
.name="NEW USB",
.id_table=usb_ids,
.probe=probe_callback,
.disconnect=disconnect_callback
};
//PROBLEM IS IN THE FUNCTION BELOW .....................
int probe_callback(struct usb_interface *intf, const struct usb_device_id *id)
{
printk(KERN_ALERT "probe callback\n");
__u16 vendor_id=id->idVendor;
__u16 product_id=id->idProduct;
printk("product id : %x vendor id : %x\n", product_id, vendor_id);
return 0;
}
//..........................
void disconnect_callback(struct usb_interface *intf)
{
printk(KERN_ALERT "disconnect callback\n");
}
static int start(void)
{
printk(KERN_ALERT "module registered\n");
int result;
result=usb_register(&USB_DRIVER);
if(result)
{
printk("error in registering usb driver...Error code = %d\n", result);
}
return result;
}
static void stop(void)
{
printk(KERN_ALERT "module unregistered\n");
usb_deregister(&USB_DRIVER);
}
module_init(start);
module_exit(stop);
The logs I get when I execute dmesg are
[ 1539.714265] probe callback
[ 1539.714277] product id : 0 vendor id : 0
Thanks in advance for any help
Your usb_id table does not contain vendor and product IDs. The id pointer passed to your probe_callback points to the element of usb_id that matched the device being probed.
You need to fill in the elements of your usb_id table properly to match the devices you are interested in, for example using the USB_DEVICE(vend, prod) macro.

Overload symbols of running process (LD_PRELOAD attachment)

I'm working on a heap profiler for Linux, called heaptrack. Currently, I rely on LD_PRELOAD to overload various (de-)allocation functions, and that works extremely well.
Now I would like to extend the tool to allow runtime attaching to an existing process, which was started without LD_PRELOADing my tool. I can dlopen my library via GDB just fine, but that won't overwrite malloc etc. I think, this is because at that point the linker already resolved the position dependent code of the already running process - correct?
So what do I do instead to overload malloc and friends?
I am not proficient with assembler code. From what I've read so far, I guess I'll somehow have to patch malloc and the other functions, such that they first call back to my trace function and then continue with their actual implementation? Is that correct? How do I do that?
I hope there are existing tools out there, or that I can leverage GDB/ptrace for that.
Just for the lulz, another solution without ptracing your own process or touching a single line of assembly or playing around with /proc. You only have to load the library in the context of the process and let the magic happen.
The solution I propose is to use the constructor feature (brought from C++ to C by gcc) to run some code when a library is loaded. Then this library just patch the GOT (Global Offset Table) entry for malloc. The GOT stores the real addresses for the library functions so that the name resolution happen only once. To patch the GOT you have to play around with the ELF structures (see man 5 elf). And Linux is kind enough to give you the aux vector (see man 3 getauxval) that tells you where to find in memory the program headers of the current program. However, better interface is provided by dl_iterate_phdr, which is used below.
Here is an example code of library that does exactly this when the init function is called. Although the same could probably be achieved with a gdb script.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/auxv.h>
#include <elf.h>
#include <link.h>
#include <sys/mman.h>
struct strtab {
char *tab;
ElfW(Xword) size;
};
struct jmpreltab {
ElfW(Rela) *tab;
ElfW(Xword) size;
};
struct symtab {
ElfW(Sym) *tab;
ElfW(Xword) entsz;
};
/* Backup of the real malloc function */
static void *(*realmalloc)(size_t) = NULL;
/* My local versions of the malloc functions */
static void *mymalloc(size_t size);
/*************/
/* ELF stuff */
/*************/
static const ElfW(Phdr) *get_phdr_dynamic(const ElfW(Phdr) *phdr,
uint16_t phnum, uint16_t phentsize) {
int i;
for (i = 0; i < phnum; i++) {
if (phdr->p_type == PT_DYNAMIC)
return phdr;
phdr = (ElfW(Phdr) *)((char *)phdr + phentsize);
}
return NULL;
}
static const ElfW(Dyn) *get_dynentry(ElfW(Addr) base, const ElfW(Phdr) *pdyn,
uint32_t type) {
ElfW(Dyn) *dyn;
for (dyn = (ElfW(Dyn) *)(base + pdyn->p_vaddr); dyn->d_tag; dyn++) {
if (dyn->d_tag == type)
return dyn;
}
return NULL;
}
static struct jmpreltab get_jmprel(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct jmpreltab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_JMPREL);
table.tab = (dyn == NULL) ? NULL : (ElfW(Rela) *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_PLTRELSZ);
table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static struct symtab get_symtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct symtab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_SYMTAB);
table.tab = (dyn == NULL) ? NULL : (ElfW(Sym) *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_SYMENT);
table.entsz = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static struct strtab get_strtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
struct strtab table;
const ElfW(Dyn) *dyn;
dyn = get_dynentry(base, pdyn, DT_STRTAB);
table.tab = (dyn == NULL) ? NULL : (char *)dyn->d_un.d_ptr;
dyn = get_dynentry(base, pdyn, DT_STRSZ);
table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
return table;
}
static void *get_got_entry(ElfW(Addr) base, struct jmpreltab jmprel,
struct symtab symtab, struct strtab strtab, const char *symname) {
ElfW(Rela) *rela;
ElfW(Rela) *relaend;
relaend = (ElfW(Rela) *)((char *)jmprel.tab + jmprel.size);
for (rela = jmprel.tab; rela < relaend; rela++) {
uint32_t relsymidx;
char *relsymname;
relsymidx = ELF64_R_SYM(rela->r_info);
relsymname = strtab.tab + symtab.tab[relsymidx].st_name;
if (strcmp(symname, relsymname) == 0)
return (void *)(base + rela->r_offset);
}
return NULL;
}
static void patch_got(ElfW(Addr) base, const ElfW(Phdr) *phdr, int16_t phnum,
int16_t phentsize) {
const ElfW(Phdr) *dphdr;
struct jmpreltab jmprel;
struct symtab symtab;
struct strtab strtab;
void *(**mallocgot)(size_t);
dphdr = get_phdr_dynamic(phdr, phnum, phentsize);
jmprel = get_jmprel(base, dphdr);
symtab = get_symtab(base, dphdr);
strtab = get_strtab(base, dphdr);
mallocgot = get_got_entry(base, jmprel, symtab, strtab, "malloc");
/* Replace the pointer with our version. */
if (mallocgot != NULL) {
/* Quick & dirty hack for some programs that need it. */
/* Should check the returned value. */
void *page = (void *)((intptr_t)mallocgot & ~(0x1000 - 1));
mprotect(page, 0x1000, PROT_READ | PROT_WRITE);
*mallocgot = mymalloc;
}
}
static int callback(struct dl_phdr_info *info, size_t size, void *data) {
uint16_t phentsize;
data = data;
size = size;
printf("Patching GOT entry of \"%s\"\n", info->dlpi_name);
phentsize = getauxval(AT_PHENT);
patch_got(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, phentsize);
return 0;
}
/*****************/
/* Init function */
/*****************/
__attribute__((constructor)) static void init(void) {
realmalloc = malloc;
dl_iterate_phdr(callback, NULL);
}
/*********************************************/
/* Here come the malloc function and sisters */
/*********************************************/
static void *mymalloc(size_t size) {
printf("hello from my malloc\n");
return realmalloc(size);
}
And an example program that just loads the library between two malloc calls.
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
void loadmymalloc(void) {
/* Should check return value. */
dlopen("./mymalloc.so", RTLD_LAZY);
}
int main(void) {
void *ptr;
ptr = malloc(42);
printf("malloc returned: %p\n", ptr);
loadmymalloc();
ptr = malloc(42);
printf("malloc returned: %p\n", ptr);
return EXIT_SUCCESS;
}
The call to mprotect is usually useless. However I found that gvim (which is compiled as a shared object) needs it. If you also want to catch the references to malloc as pointers (which may allow to later call the real function and bypass yours), you can apply the very same process to the symbol table pointed to by the DT_RELA dynamic entry.
If the constructor feature is not available for you, all you have to do is resolve the init symbol from the newly loaded library and call it.
Note that you may also want to replace dlopen so that libraries loaded after yours gets patched as well. Which may happen if you load your library quite early or if the application has dynamically loaded plugins.
This can not be done without tweaking with assembler a bit. Basically, you will have to do what gdb and ltrace do: find malloc and friends virtual addresses in the process image and put breakpoints at their entry. This process usually involves temporary rewriting the executable code, as you need to replace normal instructions with "trap" ones (such as int 3 on x86).
If you want to avoid doing this yourself, there exists linkable wrapper around gdb (libgdb) or you can build ltrace as a library (libltrace). As ltrace is much smaller, and the library variety of it is available out of the box, it will probably allow you to do what you want at lower effort.
For example, here's the best part of the "main.c" file from the ltrace package:
int
main(int argc, char *argv[]) {
ltrace_init(argc, argv);
/*
ltrace_add_callback(callback_call, EVENT_SYSCALL);
ltrace_add_callback(callback_ret, EVENT_SYSRET);
ltrace_add_callback(endcallback, EVENT_EXIT);
But you would probably need EVENT_LIBCALL and EVENT_LIBRET
*/
ltrace_main();
return 0;
}
http://anonscm.debian.org/cgit/collab-maint/ltrace.git/tree/?id=0.7.3

i2c device driver. System down when try to send data

I trying to write i2c device driver and all is gone fine:
device detected successfully
device in sysfs class with value attribute created too
then I init a hrtimer and start it
I am trying to send data from store function of attribute and it's work fine
but when I trying to send data from hrtimer callback function my system going down. Here is some code (i was remove incudes, defines and non-important code):
led-indicator.h
struct timer_job {
ktime_t period;
struct hrtimer timer;
...
};
struct ledindicator {
struct i2c_client *client;
struct device *device;
struct timer_job job;
int value;
};
led-indicator.c
include "led-indicator.h"
static struct ledindicator *li;
// device attribute [value] store function
static ssize_t value_store(struct device *device, struct device_attribute *attr, const char *buffer, size_t count)
{
int val;
kstrtoint(buffer, 10, &val);
li->value = val;
i2c_smbus_write_byte_data(li->client, 0xBA, 0x02); // this is work fine
LOG("New value is %d \n", li->value);
return count;
}
//timer callback, fire each timer_job.period
static enum hrtimer_restart send_next(struct hrtimer * tim) {
long start = jiffies;
struct timer_job *job = container_of(tim, struct timer_job, timer);
i2c_smbus_write_byte_data(li->client, 0xBA, 0x02); // this cause system fault
hrtimer_forward_now(tim, job->period);
return HRTIMER_RESTART;
}
//when device is detected I am create sysfs device and init timer.
enter code here//Also I am crate led_indicator structure and save i2c_client pointer for future use
static int li_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
li = kmalloc(sizeof(*li), GFP_KERNEL);
li->client = client;
li->value = 0;
...
// here creating device and attribute for it
...
hrtimer_init(&(li->job.timer), CLOCK_MONOTONIC, HRTIMER_MODE_REL);
li->job.timer.function = send_next;
li->job.period = ktime_set(1, 0); //1 sec
hrtimer_start(&(li->job.timer), li->job.period, HRTIMER_MODE_REL);
return ret;
}
What is wrong?

Kernel module's parameters in sysfs - quick reaction for changes

Is it possible to notify the module when one of it's sys files was changed? My task is to do a file which controls size of buffer inside the module, I want to resize the buffer when the value in the file is changed.
My other idea (if I can't notify the module) was to check the previous value each time the module is used and then resize the buffer.
Isn't this the purpose of Sysfs?
When you create a kobject and give it a representation in Sysfs (which is a directory), you then create attributes for that object which will become files in that directory. You provide a store and a show callback to the kobject, which are basically equivalents of resp. write and read.
store is what you want here. It looks like this:
ssize_t (*store)(struct kobject *kobj, struct attribute *attr,
const char *buffer, size_t size);
You receive size bytes within buffer as soon as the virtual file is written in user land.
Have a look at this module which does it (taken from here):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
struct my_attr {
struct attribute attr;
int value;
};
static struct my_attr my_first = {
.attr.name="first",
.attr.mode = 0644,
.value = 1,
};
static struct my_attr my_second = {
.attr.name="second",
.attr.mode = 0644,
.value = 2,
};
static struct attribute * myattr[] = {
&my_first.attr,
&my_second.attr,
NULL
};
static ssize_t default_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
return scnprintf(buf, PAGE_SIZE, "%d\n", a->value);
}
static ssize_t default_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t len)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
sscanf(buf, "%d", &a->value);
return sizeof(int);
}
static struct sysfs_ops myops = {
.show = default_show,
.store = default_store,
};
static struct kobj_type mytype = {
.sysfs_ops = &myops,
.default_attrs = myattr,
};
struct kobject *mykobj;
static int __init sysfsexample_module_init(void)
{
int err = -1;
mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL);
if (mykobj) {
kobject_init(mykobj, &mytype);
if (kobject_add(mykobj, NULL, "%s", "sysfs_sample")) {
err = -1;
printk("Sysfs creation failed\n");
kobject_put(mykobj);
mykobj = NULL;
}
err = 0;
}
return err;
}
static void __exit sysfsexample_module_exit(void)
{
if (mykobj) {
kobject_put(mykobj);
kfree(mykobj);
}
}
module_init(sysfsexample_module_init);
module_exit(sysfsexample_module_exit);
MODULE_LICENSE("GPL");
Also: you might want to output the buffer size to the user when the entry is read. This is usually the way of doing it. Also make sure the information (read and written) is in a human-readable format to keep up with the Unix philosophy.
Update: see this recent interesting article about Sysfs file creation written by Greg Kroah-Hartman, one of the top kernel developers.

Resources