PhilBot
PhilBot

Reputation: 60

Yocto Patch Linux Kernel In-Tree-Module with extern symbol exported from Out-Of-Tree Module

I am using Yocto to build an SD Card image for my Embedded Linux Project. The Yocto branch is Warrior and the Linux kernel version is 4.19.78-linux4sam-6.2.

I am currently working on a way to read memory from an external QSPI device in the initramfs and stick the contents into a file in procfs. That part works and I echo data into the proc file and read it out successfully later in user space Linux after the board has booted.

Now I need to use the Linux Kernel module EXPORT_SYMBOL() functionality to allow an in-tree kernel module to know about my out-of-tree custom kernel module exported symbol.

In my custom module, I do this:

static unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);

And I patched the official kernel build in a bitbake bbappend file with this:

diff -Naur kernel-source/drivers/net/usb/smsc95xx.c kernel-source.new/drivers/net/usb/smsc95xx.c
--- kernel-source/drivers/net/usb/smsc95xx.c    2020-08-04 22:34:02.767157368 +0000
+++ kernel-source.new/drivers/net/usb/smsc95xx.c        2020-08-04 23:34:27.528435689 +0000
@@ -917,6 +917,27 @@
 {
        const u8 *mac_addr;
 
+       printk("=== smsc95xx_init_mac_address ===\n");
+       printk("%x:%x:%x:%x:%x:%x\n",
+           lan9730_mac_address_buffer[0],
+           lan9730_mac_address_buffer[1],
+           lan9730_mac_address_buffer[2],
+           lan9730_mac_address_buffer[3],
+           lan9730_mac_address_buffer[4],
+           lan9730_mac_address_buffer[5]);
+       printk("=== mac_addr is set ===\n");
+       if (lan9730_mac_address_buffer[0] != 0xff &&
+           lan9730_mac_address_buffer[1] != 0xff &&
+           lan9730_mac_address_buffer[2] != 0xff &&
+           lan9730_mac_address_buffer[3] != 0xff &&
+           lan9730_mac_address_buffer[4] != 0xff &&
+           lan9730_mac_address_buffer[5] != 0xff) {
+           printk("=== SUCCESS ===\n");
+           memcpy(dev->net->dev_addr, lan9730_mac_address_buffer, ETH_ALEN);
+           return;
+       }
+       printk("=== FAILURE ===\n");
+
        /* maybe the boot loader passed the MAC address in devicetree */
        mac_addr = of_get_mac_address(dev->udev->dev.of_node);
        if (!IS_ERR(mac_addr)) {
diff -Naur kernel-source/drivers/net/usb/smsc95xx.h kernel-source.new/drivers/net/usb/smsc95xx.h
--- kernel-source/drivers/net/usb/smsc95xx.h    2020-08-04 22:32:30.824951447 +0000
+++ kernel-source.new/drivers/net/usb/smsc95xx.h        2020-08-04 23:33:50.486778978 +0000
@@ -361,4 +361,6 @@
 #define INT_ENP_TDFO_                  ((u32)BIT(12))  /* TX FIFO Overrun */
 #define INT_ENP_RXDF_                  ((u32)BIT(11))  /* RX Dropped Frame */
 
+extern unsigned char lan9730_mac_address_buffer[6];
+
 #endif /* _SMSC95XX_H */

However, the Problem is that the Kernel fails to build with this error:

|   GEN     ./Makefile
|   Using /home/me/Desktop/poky/build-microchip/tmp/work-shared/sama5d27-som1-ek-sd/kernel-source as source for kernel
|   CALL    /home/me/Desktop/poky/build-microchip/tmp/work-shared/sama5d27-som1-ek-sd/kernel-source/scripts/checksyscalls.sh
|   Building modules, stage 2.
|   MODPOST 279 modules
| ERROR: "lan9730_mac_address_buffer" [drivers/net/usb/smsc95xx.ko] undefined!

How can I refer to the Out-Of-Tree kernel module exported symbol in a patched In-Tree kernel module?

initramfs relevant code:

msg "Inserting lan9730-mac-address.ko..."
insmod /mnt/lib/modules/4.19.78-linux4sam-6.2/extra/lan9730-mac-address.ko
ls -rlt /proc/lan9730-mac-address
head -c 6 /dev/mtdblock0 > /proc/lan9730-mac-address

Out-Of-Tree module:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

const int BUFFER_SIZE = 6;

int write_length, read_length;
unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);

int read_proc(struct file *filp, char *buf, size_t count, loff_t *offp)
{
    // Read bytes (returning the byte count) until all bytes are read.
    // Then return count=0 to signal the end of the operation.

    if (count > read_length)
        count = read_length;

    read_length = read_length - count;
    copy_to_user(buf, lan9730_mac_address_buffer, count);

    if (count == 0)
        read_length = write_length;

    return count;
}

int write_proc(struct file *filp, const char *buf, size_t count, loff_t *offp)
{
    if (count > BUFFER_SIZE)
        count = BUFFER_SIZE;

    copy_from_user(lan9730_mac_address_buffer, buf, count);
    write_length = count;
    read_length = count;
    return count;
}

struct file_operations proc_fops = {
    read: read_proc,
    write: write_proc
};

void create_new_proc_entry(void)  //use of void for no arguments is compulsory now
{
    proc_create("lan9730-mac-address", 0, NULL, &proc_fops);
}

int proc_init (void) {
    create_new_proc_entry();
    memset(lan9730_mac_address_buffer, 0x00, sizeof(lan9730_mac_address_buffer));
    return 0;
}

void proc_cleanup(void) {
    remove_proc_entry("lan9730-mac-address", NULL);
}

MODULE_LICENSE("GPL");
module_init(proc_init);
module_exit(proc_cleanup);

Upvotes: 0

Views: 1135

Answers (1)

0andriy
0andriy

Reputation: 4674

There are several ways to achieve what you want (taking into account different aspects, like module can be compiled in or be a module).

  1. Convert Out-Of-Tree module to be In-Tree one (in your custom kernel build). This will require simple export and import as you basically done and nothing special is required, just maybe providing a header with the symbol and depmod -a run after module installation. Note, you have to use modprobe in-tree which reads and satisfies dependencies.
  2. Turn other way around, i.e. export symbol from in-tree module and file it in the out-of-tree. In this case you simply have to check if it has been filed or not (since it's a MAC address the check against all 0's will work, no additional flags needed)

BUT, these ways are simply wrong. The driver and even your patch clearly show that it supports OF (Device Tree) and your board has support of it. So, this is a first part of the solution, you may provide correct MAC to the network card using Device Tree.

In the case you want to change it runtime the procfs approach is very strange to begin with. Network device interface in Linux has all means to update MAC from user space at any time user wants to do it. Just use ip command, like /sbin/ip link set <$ETH> addr <$MACADDR>, where <$ETH> is a network interface, for example, eth0 and <$MACADDR> is a desired address to set.

So, if this question rather about module symbols, you need to find better example for it because it's really depends to use case. You may consider to read How to export symbol from Linux kernel module in this case? as an alternative way to exporting. Another possibility how to do it right is to use software nodes (it's a new concept in recent Linux kernel).

Upvotes: 1

Related Questions