I/O 用户模式进程的指令

I/O instructions by user-mode process

有两种访问硬件的方法:

如果用户模式进程直接想访问 I/O 而不使用系统调用,并且它知道特定的硬件,则无法通过内存映射 I/O 访问它,因为它是不在其地址 space 中,因此会发生分段错误。但是如何使用 I/O 端口来做到这一点呢?由于 I/O 端口不在内存地址 space 中,而是被处理器直接执行的指令访问,那么会发生什么情况,进程是否可以访问它们?

短版

没有。例如,在x86架构中,inout是特权指令,只能在Ring 0运行。由于user-mode在Ring 3应用运行, CPU 将在尝试执行该指令时出错。

长版

一般来说,在 OS 中强制执行内存保护和进程分离(例如 Windows、Linux、Mac OS,但不是DOS), user-mode 应用程序不能直接访问硬件,而是使用系统调用要求内核代表它们执行所需的操作。然后内核可以决定应用程序是否有权执行所述操作,该操作是否安全等。

如果 user-mode 应用程序能够执行通常只能由内核执行的任何操作,那么它就是一个 bug/backdoor 并且是内核中的一个巨大安全漏洞(或者有时 CPU本身)。

在 x86 架构上,I/O 端口指令在除实模式外的所有操作模式下都具有特权。尝试执行任何特权指令都会产生错误(具体来说,#GP、General-Protection 错误,对应于 INT 0xD)。

为了允许像 user-mode 驱动程序这样的进程工作,OS 可以通过两种方式覆盖 I/O 指令的特权状态:

  1. 使用 FLAGS 寄存器(位 12 和 13)中的 IOPL(I/O 特权级别)字段。这是一个从 0 到 3 的数字,指定可以 运行 I/O 指令的最低特权环。将 IOPL 设置为 3 将允许所有进程访问任何 I/O 端口,这是不安全的并且违背了保护其他一切(如内存)的目的。为此,操作系统通常将此字段设置为0。
  2. 使用任务状态段 (TSS) 结构中的 IOPB(I/O 权限位图)位域。这只是 65536 位的集合,每个位指定 I/O 端口是否可以访问(0)或不可以(1)。如果 user-mode 驱动程序需要访问端口 0xAB,那么 OS 可以清除 TSS 中的相应位并允许 user-mode 代码仅访问该端口,尽管 IOPL=0。如果操作系统不包含所有 65536 位,则 CPU 假定缺少的那些位具有特权,因此无法访问。