Yocto 补丁 Linux 具有从树外模块导出的外部符号的内核树内模块
Yocto Patch Linux Kernel In-Tree-Module with extern symbol exported from Out-Of-Tree Module
我正在使用 Yocto 为我的嵌入式 Linux 项目构建 SD 卡映像。 Yocto分支是Warrior,Linux内核版本是4.19.78-linux4sam-6.2.
我目前正在研究一种从 initramfs 中的外部 QSPI 设备读取内存并将内容粘贴到 procfs 中的文件的方法。那部分工作,我 echo
数据进入 proc 文件,并在板启动后在用户 space Linux 中成功读出它。
现在我需要使用 Linux 内核模块 EXPORT_SYMBOL()
功能来允许 in-tree 内核模块了解我的 out-of-tree 自定义内核模块导出符号。
在我的自定义模块中,我这样做:
static unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);
我用这个在 bitbake bbappend 文件中修补了官方内核构建:
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 */
但是,问题是内核无法构建并出现此错误:
| 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!
如何在已打补丁的 In-Tree 内核模块中引用 Out-Of-Tree 内核模块导出的符号?
initramfs相关代码:
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 模块:
#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);
有几种方法可以实现你想要的(考虑到不同的方面,比如模块可以编译成一个模块)。
- 将 Out-Of-Tree 模块转换为 In-Tree 模块(在您的自定义内核构建中)。这将需要简单的导出和导入,就像您基本上完成的那样,不需要什么特别的,只需在模块安装后提供带有符号的 header 和
depmod -a
运行 即可。请注意,您必须使用 modprobe in-tree
读取并满足依赖关系。
- 反过来,即从 in-tree 模块导出符号并将其归档到 out-of-tree。在这种情况下,您只需检查它是否已被归档(因为它是一个 MAC 地址,针对全 0 的检查将起作用,不需要额外的标志)
BUT,这些方法根本就是错误的。 driver 甚至你的补丁都清楚地表明它支持 OF(设备树)并且你的板子支持它。因此,这是解决方案的第一部分,您可以使用设备树向网卡提供正确的 MAC。
万一您想更改它运行时间,procfs 方法一开始就很奇怪。 Linux 中的网络设备接口可以随时从用户 space 更新 MAC。只需使用 ip
命令,如 /sbin/ip link set <$ETH> addr <$MACADDR>
,其中 <$ETH>
是网络接口,例如 eth0 和 <$MACADDR>
是所需的要设置的地址。
因此,如果这个问题与模块符号有关,则您需要为其找到更好的示例,因为它确实取决于用例。您可以考虑阅读 How to export symbol from Linux kernel module in this case? 作为导出的替代方法。另一种正确的方法是使用软件节点(这是最近 Linux 内核中的一个新概念)。
我正在使用 Yocto 为我的嵌入式 Linux 项目构建 SD 卡映像。 Yocto分支是Warrior,Linux内核版本是4.19.78-linux4sam-6.2.
我目前正在研究一种从 initramfs 中的外部 QSPI 设备读取内存并将内容粘贴到 procfs 中的文件的方法。那部分工作,我 echo
数据进入 proc 文件,并在板启动后在用户 space Linux 中成功读出它。
现在我需要使用 Linux 内核模块 EXPORT_SYMBOL()
功能来允许 in-tree 内核模块了解我的 out-of-tree 自定义内核模块导出符号。
在我的自定义模块中,我这样做:
static unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);
我用这个在 bitbake bbappend 文件中修补了官方内核构建:
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 */
但是,问题是内核无法构建并出现此错误:
| 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!
如何在已打补丁的 In-Tree 内核模块中引用 Out-Of-Tree 内核模块导出的符号?
initramfs相关代码:
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 模块:
#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);
有几种方法可以实现你想要的(考虑到不同的方面,比如模块可以编译成一个模块)。
- 将 Out-Of-Tree 模块转换为 In-Tree 模块(在您的自定义内核构建中)。这将需要简单的导出和导入,就像您基本上完成的那样,不需要什么特别的,只需在模块安装后提供带有符号的 header 和
depmod -a
运行 即可。请注意,您必须使用modprobe in-tree
读取并满足依赖关系。 - 反过来,即从 in-tree 模块导出符号并将其归档到 out-of-tree。在这种情况下,您只需检查它是否已被归档(因为它是一个 MAC 地址,针对全 0 的检查将起作用,不需要额外的标志)
BUT,这些方法根本就是错误的。 driver 甚至你的补丁都清楚地表明它支持 OF(设备树)并且你的板子支持它。因此,这是解决方案的第一部分,您可以使用设备树向网卡提供正确的 MAC。
万一您想更改它运行时间,procfs 方法一开始就很奇怪。 Linux 中的网络设备接口可以随时从用户 space 更新 MAC。只需使用 ip
命令,如 /sbin/ip link set <$ETH> addr <$MACADDR>
,其中 <$ETH>
是网络接口,例如 eth0 和 <$MACADDR>
是所需的要设置的地址。
因此,如果这个问题与模块符号有关,则您需要为其找到更好的示例,因为它确实取决于用例。您可以考虑阅读 How to export symbol from Linux kernel module in this case? 作为导出的替代方法。另一种正确的方法是使用软件节点(这是最近 Linux 内核中的一个新概念)。