--- title: Firmadyne MIPS kernel-v2.6.32 procfs_stubs.c tags: firmadyne lang: zh_tw --- --- # Firmadyne MIPS kernel-v2.6.32 procfs_stubs.c Source Code: https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/drivers/firmadyne/procfs_stubs.c 紀錄追 Code 過程 --- **目錄:** [TOC] ## procfs vs initramfs 參考: [procfs wiki](https://zh.wikipedia.org/wiki/Procfs) > 在許多類 Unix 電腦系統中, procfs 是 行程 (process) 檔案系統 (file system) 的縮寫,包含一個偽檔案系統(啟動時動態生成的檔案系統),用於通過核心存取行程資訊。這個檔案系統通常被掛載到 /proc 目錄。由於 /proc 不是一個真正的檔案系統,它也就不占用儲存空間,只是占用有限的記憶體。 procfs 是種存在於 RAM 的虛擬檔案系統。 參考: [鳥哥的 Linux 私房菜 -- 第十九章、開機流程、模組管理與 Loader](http://linux.vbird.org/linux_basic/0510osloader.php#startup_loader) 在 `載入核心偵測硬體與 initramfs 的功能` 一節中提到 > 一般來說,非必要的功能且可以編譯成為模組的核心功能,目前的 Linux distributions 都會將他編譯成為模組。 因此 USB, SATA, SCSI... 等磁碟裝置的驅動程式通常都是以模組的方式來存在的。 > 問題是,核心根本不認識 SATA 磁碟,所以需要載入 SATA 磁碟的驅動程式, 否則根本就無法掛載根目錄。但是 SATA 的驅動程式在 /lib/modules 內,你根本無法掛載根目錄又怎麼讀取到 /lib/modules/ 內的驅動程式 > 虛擬檔案系統 (Initial RAM Disk 或 Initial RAM Filesystem) 一般使用的檔名為 /boot/initrd 或 /boot/initramfs ,這個檔案的特色是,他也能夠透過 boot loader 來載入到記憶體中,然後這個檔案會被解壓縮並且在記憶體當中模擬成一個根目錄, 且此模擬在記憶體當中的檔案系統能夠提供一支可執行的程式,透過該程式來載入開機過程中所最需要的核心模組, 通常這些模組就是 USB, RAID, LVM, SCSI 等檔案系統與磁碟介面的驅動程式啦!等載入完成後, 會幫助核心重新呼叫 systemd 來開始後續的正常開機流程。 我在想或許 procfs 的機制跟這幾段話有關。 所以小小姑狗了一下,兩者是否不同 參考: [ramfs, tmpfs, rootfs, initramfs的区别](https://woshijpf.github.io/%E5%86%85%E6%A0%B8/2017/06/14/ramfs-rootfs-initramfs%E7%9A%84%E5%8C%BA%E5%88%AB.html) 發現不一樣 procfs 是用來取得跟 process 有關的資訊 e.g. fd 也可以反過來輸入資訊給 process by 改掉裡面的檔案 e.g. [關閉 ASLR](https://askubuntu.com/questions/318315/how-can-i-temporarily-disable-aslr-address-space-layout-randomization) 而鳥哥寫的是 initramfs > initramfs 是一种以 cpio 格式压缩后的 rootfs 文件系统 > 并启动内核之后,内核接着就对 cpio 格式的 initramfs 进行解压,并将解压后得到的 rootfs 加载进内存 .... 從這段話可以說明,initramfs 本身是 rootfs,且會被 kernel load 到 ram 中 兩者都是存在於 ram,但本質和目的不一樣 | | procfs | initramfs | | -------- | -------- | -------- | | 存在於 | ram | ram | | 本質 | procfs | 壓縮過的 rootfs | | 目的 | 與 processes 溝通 | 開機流程一環 | ## procfs_stubs.c Line 9 定義了 Macro STUB_ENTRIES 裡頭有 Macro DIR、FILE ```c=9 #define STUB_ENTRIES \ FILE(blankstatus, read, write, NULL) \ FILE(btnCnt, read, write, NULL) \ FILE(br_igmpProxy, read, write, NULL) \ FILE(BtnMode, read, write, NULL) \ FILE(gpio, read, write, NULL) \ FILE(led, read, write, NULL) \ /* Used by "Firmware_TEW-435BRM_0121e.zip" (12973) */ \ FILE(push_button, read, write, NULL) \ FILE(rtk_promiscuous, read, write, NULL) \ FILE(rtk_vlan_support, read, write, NULL) \ FILE(RstBtnCnt, read, write, NULL) \ FILE(sw_nat, read, write, NULL) \ DIR(simple_config, NULL) \ FILE(reset_button_s, read, write, simple_config_dir) \ DIR(quantum, NULL) \ FILE(drv_ctl, read, write, quantum_dir) \ DIR(rt3052, NULL) \ DIR(mii, rt3052_dir) \ FILE(ctrl, read, write, mii_dir) \ FILE(data, read, write, mii_dir) ``` --- 在 Line 59 ~ 70 展開 Macro ```c=59 #define DIR(a, b) \ static struct proc_dir_entry *a##_dir; #define FILE(a, b, c, d) \ static const struct file_operations a##_fops = { \ .read = b, \ .write = c, \ }; STUB_ENTRIES #undef FILE #undef DIR ``` 以 `DIR(mii, rt3052_dir)` 來說,會被展開為 ```c static struct proc_dir_entry *mii_dir; ``` 以 `FILE(ctrl, read, write, mii_dir)` 來說,會被展開為 ```c static const struct file_operations ctrl_fops = { .read = read, .write = write, }; ``` --- 在 Line 72 的 `register_procfs_stubs()` 中 ```c=75 if (!procfs) { return -EINVAL; } ``` extern 變數 procfs 初始化於 [firmadyne.c](https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/drivers/firmadyne/firmadyne.c) 中,為 1 Line 79 ~ 94 展開 Macro ```c=79 #define DIR(a, b) \ if (!(a##_dir = proc_mkdir(#a, b))) { \ printk(KERN_WARNING MODULE_NAME": Cannot register procfs directory: %s!\n", #a); \ ret = -1; \ } #define FILE(a, b, c, d) \ if (!proc_create_data(#a, 0666, d, &a##_fops, NULL)) { \ printk(KERN_WARNING MODULE_NAME": Cannot register procfs file: %s!\n", #a); \ ret = -1; \ } STUB_ENTRIES #undef FILE #undef DIR ``` 以 `DIR(mii, rt3052_dir)` 來說,會被展開為 ```c if (!(mii_dir = proc_mkdir("mii", rt3052_dir))) { printk(KERN_WARNING MODULE_NAME": Cannot register procfs directory: %s!\n", "mii"); ret = -1; } ``` 在變數 rt3052_dir 所代表的目錄底下創立名為 `mii` 的目錄 並用變數 mii_dir 存著,代表著這個目錄 若失敗才會進 if 以 `FILE(ctrl, read, write, mii_dir)` 來說,會被展開為 ```c if (!proc_create_data("ctrl", 0666, mii_dir, &ctrl_fops, NULL)) { printk(KERN_WARNING MODULE_NAME": Cannot register procfs file: %s!\n", "ctrl"); ret = -1; } ``` 在變數 mii_dir 所代表著的目錄底下創立檔名為 ctrl 的檔案,權限為 0666,相關 file operations 位置在 ctrl_fops 參考 [Linux内核中的proc文件系统](http://www.embeddedlinux.org.cn/emb-linux/file-system/201703/27-6340.html) ``` struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops, void *data) ``` - name 你要创建的文件名。 - mode 为创建的文件指定权限 - parent 为你要在哪个文件夹下建立名字为name的文件,如:init_net.proc_net是要在/proc/net/下建立文件。 - proc_fops 为 struct file_operations - data 保存私有数据的指针,如不要为NULL。 而 Line 99 的 `unregister_procfs_stubs()` 只是在做相反的事情 有創就有刪 ## 總結 這份 Code 主要拿來創 procfs `FILE(filename, read, write, dir)` 會在 `dir` 底下創檔名為 `filename` 的檔案 `DIR(subdir, rootdir)` 會在 `/proc/${rootdir}` 底下創名為 `subdir` 的目錄