Linux module installation and uninstallation

Module implementation

Shinken my_module.c Message

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>

static int __init module_base_init(void)
{
    
    
    printk("base module init!\r\n");

    return 0;
}

static void __exit module_base_exit(void)
{
    
    
    printk("base module exit!\r\n");
}

module_init(module_base_init);
module_exit(module_base_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");

Makefile for compiling modules

Shinken Makefile Message

# 指定内核路径
KERNELDIR := /home/tyustli/code/open_source/kernel/linux-6.5.7
# 指定当前路径
CURRENT_PATH := $(shell pwd)
# 指定编译的模块名
obj-m := my_module.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

Compilation error resolution

At this time, if you execute it directly in the module path make, there will be the following warnings and errors,cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: undefined symbol: _Z16gen_load_tp_hardP7rtx_def

The complete log is as follows

make -C /home/tyustli/code/open_source/kernel/linux-6.5.7 M=/home/tyustli/code/qemu_code/linux_driver/0001_module_init modules
make[1]: Entering directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: arm-none-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 10.3-2021.07 (arm-10.29)) 10.3.1 20210621
  You are using:           gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
  CC [M]  /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.o
cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: undefined symbol: _Z16gen_load_tp_hardP7rtx_def
make[3]: *** [scripts/Makefile.build:243: /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.o] Error 1
make[2]: *** [/home/tyustli/code/open_source/kernel/linux-6.5.7/Makefile:2037: /home/tyustli/code/qemu_code/linux_driver/0001_module_init] Error 2
make[1]: *** [Makefile:237: __sub-make] Error 2
make[1]: Leaving directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'
make: *** [Makefile:11: kernel_modules] Error 2

This is because when compiling the kernel earlier, ARCH and CROSS_COMPILE were passed in through the shell script to compile the module. These were not specified when .

The solution is to define these two variables directly at the top level of the kernel Makefile (a bit crude)

ARCH = arm
CROSS_COMPILE = arm-none-linux-gnueabihf-

Insert image description here

Module compilation log

make -C /home/tyustli/code/open_source/kernel/linux-6.5.7 M=/home/tyustli/code/qemu_code/linux_driver/0001_module_init modules
make[1]: Entering directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'
  CC [M]  /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.o
  MODPOST /home/tyustli/code/qemu_code/linux_driver/0001_module_init/Module.symvers
  CC [M]  /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.mod.o
  LD [M]  /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.ko
make[1]: Leaving directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'

Insert image description here

automation

After the module is compiled, the best way is to set rootfs to nfs, so that the compiled ko can be directly placed in the network file system and the kernel can be started directly.

Since the current form of ubuntu + wifi is used, if you want qemu to connect to the Internet, you need to establish a network bridge. However, wifi the network card is not in AP mode. The wireless network card in Managed mode does not have enough information to act as a bridge and can only be converted to master mode

iwconfig wlp2s0 mode master

As a result, the network card does not support

Error for wireless request "Set Mode" (8B06) :
    SET failed on device wlp2s0 ; Operation not permitted.

This can only be achieved through software hostapd. . . . . .

This is a bit complicated and goes beyond the purpose of studying linux drivers.

The solution is to copy the generated ko file to the root file directory after each generationxxx.ko, and then repackage the root file systemrootfs . In this way, after Linux starts, the module will be in the rootfs root file system

# 将生成的 .ko 文件拷贝到根文件系统的 roorfs 中
cp ./my_module.ko /home/tyustli/code/open_source/busybox/rootfs/lib/modules/6.5.7+/

# 切换到根文件系统目录
cd /home/tyustli/code/open_source/busybox
# 生成虚拟 SD 卡系统镜像
sudo dd if=/dev/zero of=rootfs.ext3 bs=1M count=32
# 格式化镜像
sudo mkfs.ext3 rootfs.ext3

#将文件复制到镜像中
sudo mkdir tmpfs_rootfs
sudo mount -t ext3 rootfs.ext3 tmpfs_rootfs/ -o loop
sudo cp -r rootfs/*  tmpfs_rootfs/
sudo umount tmpfs_rootfs
rmdir tmpfs_rootfs

Here rootfs is created, restart linux

sudo qemu-system-arm -M vexpress-a9 -m 512M \
-kernel /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/zImage \
-dtb /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb -nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0" \
-sd /home/tyustli/code/open_source/busybox/rootfs.ext3

Script source code

#                          参数解析
# ./my_module_build.sh      para1            para2(可选)
#   脚本名称            指定模块路径     是否执行 make clean 命令

# 判断 shell 脚本有几个参数,如果没有指定 module 目录, shell 脚本就报错退出
if [ $# -eq 0 ]; then
    echo "Incorrect number of arguments for command
Usage: my_module_build.sh <module_dir>  build your own module"
    exit
fi

# 切换到指定的目录
cd $1

# 如果是清除工程,就执行 make clean 命令
if [ "$2" == "clean" ]; then
    make clean
    exit
fi

# 编译指定目录的模块
make
# 将生成的 .ko 文件拷贝到根文件系统的 roorfs 中
cp ./my_module.ko /home/tyustli/code/open_source/busybox/rootfs/lib/modules/6.5.7+/

# 切换到根文件系统目录
cd /home/tyustli/code/open_source/busybox
# 生成虚拟 SD 卡系统镜像
sudo dd if=/dev/zero of=rootfs.ext3 bs=1M count=32
# 格式化镜像
sudo mkfs.ext3 rootfs.ext3

#将文件复制到镜像中
sudo mkdir tmpfs_rootfs
sudo mount -t ext3 rootfs.ext3 tmpfs_rootfs/ -o loop
sudo cp -r rootfs/*  tmpfs_rootfs/
sudo umount tmpfs_rootfs
rmdir tmpfs_rootfs

# 切换回指定的目录
cd $1

# 启动 kernel
sudo qemu-system-arm -M vexpress-a9 -m 512M \
-kernel /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/zImage \
-dtb /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb -nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0" \
-sd /home/tyustli/code/open_source/busybox/rootfs.ext3

usage

./my_module_build.sh 0001_module_init/

Module installation

Check if the module file exists

ls /lib/modules/6.5.7+/my_module.ko

Module installation

Linux device driver can be dynamically loaded using insmod or modprobe. Insmod can only load one specific driver at a time, and the driver is required Absolute path, while modprobe can load all dependent drivers into the kernel at one time without requiring the specific address of the driver. But you need to copy the driver to the/lib/modules/$(uname -r)/ directory.

insmod

insmod /lib/modules/6.5.7+/my_module.ko

Insert image description here
base module initThe information printed by and module_base_init is consistent

modprobe

Of course you can also use

modprobe my_module

Be careful not to bring .ko extension

If you are prompted that commands such as modprobe depmod cannot be found, you need to enable this function in busybox and repackage the root file system

Insert image description here

Module uninstall

rmmod my_module

Insert image description here
base module exitThe information printed by and module_base_exit is consistent

Configuration header file path

Used inmy_module.c file#include contains some kernel header files, so how to jump to these header files can be done with the help ofC/C++ plug-in, orclangd plug-in

C/C++ plugin

Ctrl + Shift + p EnterC/C++:Edit Configurations(JSON), a .vscode file will be automatically created under the current path, and c_cpp_properties.json will be generated, in the file Enter

{
    
    
    "configurations": [
        {
    
    
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/home/tyustli/code/open_source/kernel/linux-6.5.7/include",
                "/home/tyustli/code/open_source/kernel/linux-6.5.7/include/uapi",
                "/home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/include",
                "/home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/include/generated",
                "/home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/include/generated/uapi"
            ],
            "defines": [
            ],
            "compilerPath": "/home/tyustli/cross_tool/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc",
            "cStandard": "c17",
            "cppStandard": "gnu++17",
            "intelliSenseMode": "linux-gcc-arm"
        }
    ],
    "version": 4
}

After this configuration, the header file can jump normally.

can also generate files in .vscode and enter in itsettings.json

{
    
    
    "search.exclude": {
    
    
        "**/node_modules": true,
        "**/bower_components": true,
        "**/*.su":true,
        "Documentation":true,
    },
    "files.exclude": {
    
    
        "**/.git": true,
        "**/.svn": true,
        "**/.hg": true,
        "**/CVS": true,
        "**/.DS_Store": true,
        "**/*.su":true,
        "Documentation":true,
    },
    "files.associations": {
    
    
        "map.h": "c",
        "module.h": "c",
        "init.h": "c",
        "types.h": "c",
        "kernel.h": "c",
        "kobject.h": "c",
        "sysfs.h": "c",
        "kernfs.h": "c",
        "idr.h": "c",
        "radix-tree.h": "c",
        "xarray.h": "c",
        "mm.h": "c",
        "sched.h": "c",
        "seccomp.h": "c",
        "*.tcc": "c",
        "compiler_attributes.h": "c"
    }
}

clangd plugin

Use the following command to generate compile_commands.json file

bear -- make

Configuration clangd, due to conflicts between clangd and c/c++, in settings.json

    "C_Cpp.intelliSenseEngine": "disabled",

Placement c_cpp_properties.json Sentence

"compileCommands": "${workspaceFolder}/linux_driver/0001_module_init/compile_commands.json"

A .cache directory will eventually be generated

Guess you like

Origin blog.csdn.net/tyustli/article/details/134100824