2015年8月22日 星期六

ioctl 系統呼叫

驅動程式除了 read()/write() 外,通常可以透過 ioctl() 系統呼叫進行一些控制或設定,如鎖門、退片、回報錯誤、改變 baud rate、或 self destruct 等。

在 user space 格式如下:

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long cmd, ...);

其中 fd 是開的 file descriptor,cmd 是指令碼,和之後可能的參數。在這裡,... 只會有一個選擇性的第三個參數,看 cmd 需要來定,可以沒有、可以是整數、可以是指標傳送任意量的任意資料。

每個驅動程式的 ioctl cmd 基本上是不同的、通常未文件化的,而無法 audit these calls in any sort of comprehensive manner. It is also difficult to make the unstructured ioctl arguments work identically on all systems; for example, consider 64-bit systems with a user-space process running in 32-bit mode. 以致於有實作其它各種方式的控制操作的驅求,包括 embedding commands into the data stream 或使用 sysfs 或驅動程式特有的 virtual filesystems 等。

基本上 cmd 的編碼只要和對應的驅動程式配合好就好,但如果 cmd 編號在整個系統是唯一的,這樣 cmd 發給錯誤的 device 就可以偵測到,不處理而回 EINVAL。為協助建立唯一的 cmd 編號,在 <linux/ioctl.h> 將 cmd 分成幾個欄位:

  • 2-bit dir:bit 0 = write,bit 1 = read
  • 14-bit size:資料長度,並不強制使用,kernel 本身並不檢查。
  • 8-bit type:magic number (神奇數字),Documentation/ioctl-number.txt 有列表,可用來選擇不重複的 magic number。
  • 8-bit number:command number (指令編號)

0x54 is just a magic number to make these relatively unique ('T')

ioctl() 和 read() 或 write() 一樣,可能需要 copy_from_user() 在 user-space 和 kernel-space 之間搬移資料。

如果只是設定的話,第三個參數、甚至 cmd 可直接放設定的資料,只要大小不要超過 unsigned long,未必需要是一個指標指到設定的資料。這樣不用取資料位址,也不用 copy_from_user(),效能會好一點點吧。

ioctl() 錯誤時回傳 -1,並設定 errno。如果只是讀取 1 個或 2 個 byte,也可以直接用回傳正值,不用第三個參數指標指到回傳 buffer,也不用 copy_to_user(),效能會好一點點吧。

Kernel 內部是執行 vfs_ioctl(filp, fd, cmd, arg),如果需要 security 檢查會先執行 security_file_ioctl()。vfs_ioctl() 會處理 cmd FIOCLEX、FIONCLEX、FIONBIO、FIOASYNC、和 FIOQSIZE,這些 cmd 的 magic 使用 'T',有些架構用 'f'。其它 cmd,如果是一般檔案則執行 file_ioctl(),否則執行 do_ioctl()。do_ioctl() 執行 unlocked_ioctl() 或 Big Kernel Lock 執行 ioctl()。

另一個會呼叫 vfs_ioctl() 的地方是 compat_sys_ioctl(),在 64 位元系統才有,有一些相容設計。

在驅動程式的格式是

int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

inode 和 filp 來自應用程式提供的 file descriptor fd。cmd 不變地來自應用程式。unsigned long arg 來自應用程式的第三個參數,無論是整數或指標、或者無。

大部分驅動程式實作,會有個針對 cmd 的 大 switch,選擇對應的行為,不同 cmd 有不同數值,通常在 header 檔定義成 symbolic names,給驅動程式和應用程式共用。

參考

  1. LDD ch. 6
  2. Linux Kernel

沒有留言:

張貼留言

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...