Try   HackMD

2024q1 Homework6 (integration)

contributed by < mesohandsome >

開發環境

$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0

$ lscpu
Architecture:                       x86_64
CPU op-mode(s):                     32-bit, 64-bit
Byte Order:                         Little Endian
Address sizes:                      39 bits physical, 48 bits virtual
CPU(s):                             12
On-line CPU(s) list:                0-11
Thread(s) per core:                 2
Core(s) per socket:                 6
Socket(s):                          1
NUMA node(s):                       1
Vendor ID:                          GenuineIntel
CPU family:                         6
Model:                              151
Model name:                         12th Gen Intel(R) Core(TM) i5-12400
Stepping:                           2
CPU MHz:                            2500.000
CPU max MHz:                        4400.0000
CPU min MHz:                        800.0000
BogoMIPS:                           4992.00
Virtualization:                     VT-x
L1d cache:                          288 KiB
L1i cache:                          192 KiB
L2 cache:                           7.5 MiB
L3 cache:                           18 MiB
NUMA node0 CPU(s):                  0-11

閱讀〈Linux 核心模組運作原理〉

編譯用命令

# make
make -C /lib/modules/`uname -r`/build/ M=`pwd` modules

# make clean
make -C /lib/modules/`uname -r`/build/ M=`pwd` clean

什麼是 Character / Block Device Driver?

A character device typically transfers data to and from a user application — they behave like pipes or serial ports, instantly reading or writing the byte data in a character-by-character stream. They provide the framework for many typical drivers, such as those that are required for interfacing to serial communications, video capture, and audio devices.

The main alternative to a character device is a block device. Block devices behave in a similar fashion to regular files, allowing a buffered array of cached data to be viewed or manipulated with operations such as reads, writes, and seeks. Both device types can be accessed through device files that are attached to the file system tree.

insmod

使用 man 在 Manual page 中可以看到關於 insmod 的簡述。

insmod - Simple program to insert a module into the Linux Kernel

在撰寫核心模組時,會使用巨集 module_init(init_function_name),讓核心找到寫好的 init 函式,以 int init_module(void) __attribute__((alias(#initfn)));init_module 取一個別名,也就是我們的 init 函式名稱,因此呼叫 init_module 等同於呼叫自己寫的函式。

而後透過 sudo strace insmod hello.ko 觀察發現,一開始 insmod 會開啟 fibdrv.ko,並得到該檔案的 fd,最後會執行 finit_module(3, "", 0),當中會透過 load_module() 呼叫我們的 init 函式並初始化。

使用 List API 找到核心模組的符號

/kernel/module.c 中:

static struct module *mod_find(unsigned long addr)
{
	struct module *mod;

	list_for_each_entry_rcu(mod, &modules, list) {
		if (within_module(addr, mod))
			return mod;
	}

	return NULL;
}

MODULE_LICENSE 授權條款影響

如果模組使用了 MODULE_LICENSE("GPL") 或類似的 GPL 授權,這樣的模組可以無限制的訪問核心中所有標記為 EXPORT_SYMBOL_GPL 的符號。

使用 Proprietary 的 non-GPL 的話,就不能用標記為 EXPORT_SYMBOL_GPL 的符號了。

strace

execve("/usr/sbin/insmod", ["insmod", "hello.ko"], 0x7ffdebbff978 /* 25 vars */) = 0
brk(NULL)                               = 0x55f7c552a000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffdcba62cf0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=75375, ...}) = 0
mmap(NULL, 75375, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4410ae2000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libzstd.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@B\0\0\0\0\0\0"..., 832) = 832
...
openat(AT_FDCWD, "/home/scream/linux2024/workspace/hello/hello.ko", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1", 6)               = 6
lseek(3, 0, SEEK_SET)                   = 0
fstat(3, {st_mode=S_IFREG|0664, st_size=215504, ...}) = 0
mmap(NULL, 215504, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f44104e2000
finit_module(3, "", 0)                  = 0
munmap(0x7f44104e2000, 215504)          = 0
close(3)                                = 0
exit_group(0)                           = ?

open(), read(), write(), mmap()

閱讀《The Linux Kernel Module Programming Guide》(LKMPG)

疑惑

kernel 會自動分配 major number 給 device driver,那 device driver 要怎麼用 minor number 判斷目前的裝置為何?