[Kernel driver] Kernel module transfer participates in symbol sharing

00. Table of Contents

01. Overview

As an extensible dynamic module, the kernel module provides flexibility to the Linux kernel, but sometimes we need to pass different parameters to the kernel according to different application scenarios, such as turning on debugging mode in the program, setting detailed output mode, and formulating specific Module-related options can be used to change the behavior of the module in the form of parameters.

Insert image description here

02. module_param

The Linux kernel provides a macro to implement module parameter passing

module_param function (kernel source code/include/linux/moduleparam.h)

/**
 * module_param - typesafe helper for a module/cmdline parameter
 * @value: the variable to alter, and exposed parameter name.
 * @type: the type of the parameter
 * @perm: visibility in sysfs.
 *
 * @value becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
 * ".") the kernel commandline parameter.  Note that - is changed to _, so
 * the user can use "foo-bar=1" even for variable "foo_bar".
 *
 * @perm is 0 if the the variable is not to appear in sysfs, or 0444
 * for world-readable, 0644 for root-writable, etc.  Note that if it
 * is writable, you may need to use kernel_param_lock() around
 * accesses (esp. charp, which can be kfreed when it changes).
 *
 * The @type is simply pasted to refer to a param_ops_##type and a
 * param_check_##type: for convenience many standard types are provided but
 * you can create your own by defining those variables.
 *
 * Standard types are:
 *  byte, short, ushort, int, uint, long, ulong
 *  charp: a character pointer
 *  bool: a bool, values 0/1, y/n, Y/N.
 *  invbool: the above, only sense-reversed (N = true).
 */
#define module_param(name, type, perm)              \
    module_param_named(name, name, type, perm)


/**
 * module_param_array - a parameter which is an array of some type
 * @name: the name of the array variable
 * @type: the type, as per module_param()
 * @nump: optional pointer filled in with the number written
 * @perm: visibility in sysfs
 *
 * Input and output are as comma-separated values.  Commas inside values
 * don't work properly (eg. an array of charp).
 *
 * ARRAY_SIZE(@name) is used to determine the number of elements in the
 * array, so the definition must be visible.
 */
#define module_param_array(name, type, nump, perm)      \
    module_param_array_named(name, name, type, nump, perm)

The module_param function in the above code needs to pass in three parameters:

  • name: the variable name we defined;
  • type: The type of parameter. Currently, the parameter types supported by the kernel include byte, short, ushort, int, uint, long, ulong, charp, bool, and invbool. Among them, charp represents a character pointer, bool is a Boolean type, and its value can only be 0 or 1; invbool is an inverse Boolean type, and its value can only be 0 or 1, but the true value represents 0 and false represents 1. When the variable is of type char, the parameter passed can only be byte, and when it is char *, it can only be charp.
  • perm: represents the permissions of the file. See the table below for specific parameter values.
user group Flag bit explain
Current user S_IRUSR User has read permission
S_IWUSR User has write permission
Current user group S_IRGRP Other users in the current user group have read permissions
S_IWUSR Other users in the current user group have write permissions
other users S_IROTH Other users have read permission
S_IWOTH Other users have write permissions

The above file permissions only have no settings for executable permissions. Please note that this file does not allow it to have executable permissions. If the parameter value S_IXUGO indicating executable permission is forcibly assigned to this parameter, the final generated kernel module will prompt an error when loading.

Program example

static int itype = 0;
module_param(itype, int, 0);

static bool btype = 0;
module_param(btype, bool, 0644);

static char ctype = 0;
module_param(ctype, byte, 0);

static char  *stype = 0;
module_param(stype, charp, 0644);

static int __init param_init(void)
{
    
    
   printk(KERN_ALERT "param init!\n");
   printk(KERN_ALERT "itype=%d\n",itype);
   printk(KERN_ALERT "btype=%d\n",btype);
   printk(KERN_ALERT "ctype=%d\n",ctype);
   printk(KERN_ALERT "stype=%s\n",stype);
   return 0;
}
  • Lines 1-11: Define four common variables and then use the module_param macro to declare these four parameters
  • Lines 13-21: And output the four parameters declared above in param_init.

03. Symbol sharing code

The kernel source code about exported symbols has been analyzed in detail before. Symbols refer to functions and variables exported in the kernel module. When the module is loaded, they are recorded in the public kernel symbol table for calls by other modules. This mechanism allows us to use layered ideas to solve some complex module designs. When we write a driver, we can divide the driver into several kernel modules according to functions, and use symbol sharing to implement interface calls and variable sharing between modules.

/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec)               \
    extern typeof(sym) sym;                 \
    __CRC_SYMBOL(sym, sec)                  \
    static const char __kstrtab_##sym[]         \
    __attribute__((section("__ksymtab_strings"), aligned(1))) \
    = VMLINUX_SYMBOL_STR(sym);              \
    extern const struct kernel_symbol __ksymtab_##sym;  \
    __visible const struct kernel_symbol __ksymtab_##sym    \
    __used                          \
    __attribute__((section("___ksymtab" sec "+" #sym), unused)) \
    = {
      
       (unsigned long)&sym, __kstrtab_##sym }

#define EXPORT_SYMBOL(sym)                  \
    __EXPORT_SYMBOL(sym, "")

The EXPORT_SYMBOL macro is used to export symbols to the kernel, so that other modules can also use the symbols we export. The following is a piece of code to introduce how to use a module to export symbols.

Program example

static int itype = 0;
module_param(itype, int, 0);

EXPORT_SYMBOL(itype);

int my_add(int a, int b)
{
    
    
   return a + b;
}

EXPORT_SYMBOL(my_add);

int my_sub(int a, int b)
{
    
    
   return a - b;
}

EXPORT_SYMBOL(my_sub);
  • Lines 2-3: The parameter type is defined and exported through the EXPORT_SYMBOL macro
  • Lines 7-12: and my_add, and exported through the EXPORT_SYMBOL macro
  • Lines 14-21: my_sub function and exported through EXPORT_SYMBOL macro

04. Program example

add.h

#ifndef __ADD_H__
#define __ADD_H__

extern int itype;

int my_add(int a, int b);
int my_sub(int a, int b);

#endif

insmod test.ko itype=123 btype=1 ctype=200 stype=abc

add.c

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

#include "add.h"

static int __init cal_init(void)
{
    
    
    printk(KERN_ALERT "cal init!\n");
    printk(KERN_ALERT "itype+1 = %d, itype-1 = %d\n", my_add(itype, 1), my_sub(itype, 1));    

    return 0;
}

static void __exit cal_exit(void)
{
    
    
    printk(KERN_ALERT "cal exit!\n");
}


module_init(cal_init);
module_exit(cal_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("uplooking");
MODULE_DESCRIPTION("cal module");
MODULE_ALIAS("cal_module");

test.c

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

static int itype = 0;
module_param(itype, int, 0);

static bool btype = 0;
module_param(btype, bool, 0700);

static char ctype = 0;
module_param(ctype, byte, 0);

static char  *stype = 0;
module_param(stype, charp, 0644);

static int __init param_init(void)
{
    
    
   printk(KERN_ALERT "param init!\n");
   printk(KERN_ALERT "itype=%d\n",itype);
   printk(KERN_ALERT "btype=%d\n",btype);
   printk(KERN_ALERT "ctype=%d\n",ctype);
   printk(KERN_ALERT "stype=%s\n",stype);
   return 0;
}

static void __exit param_exit(void)
{
    
    
   printk(KERN_ALERT "module exit!\n");
}

EXPORT_SYMBOL(itype);

int my_add(int a, int b)
{
    
    
   return a+b;
}

EXPORT_SYMBOL(my_add);

int my_sub(int a, int b)
{
    
    
   return a-b;
}

EXPORT_SYMBOL(my_sub);

module_init(param_init);
module_exit(param_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("uplooking");
MODULE_DESCRIPTION("module_param");
MODULE_ALIAS("module_param");

Makefile

CROSS_COMPILE=aarch64-linux-gnu-gcc

obj-m := test.o add.o

all:
	$(MAKE) -C $(KERNEL_DIR) M=`pwd` modules

.PHONE:clean
clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean

05. To be continued

06. Appendix

Guess you like

Origin blog.csdn.net/dengjin20104042056/article/details/132884351
Recommended