使用和不使用 IO 位图创建适当的任务状态段 (TSS) 结构?

Creating a proper Task State Segment (TSS) structure with and without an IO Bitmap?

阅读 Intel 和 AMD 之间的文档并查看代码有时很难理解如何创建没有 IO 端口位图 (IOPB) 的正确任务状态段 (TSS)。对于使用 IOPB 创建 TSS 似乎也存在混淆,因为关于 IO 位图 (IOPB) 是否需要尾随 0xff 字节似乎不明确。

我知道 TSS 和 TSS 描述符(在 GDT 中)之间存在依赖关系。 TSS 描述符控制 TSS 的基地址和限制。描述符中的限制比结构的实际大小小一(本质上类似于 GDT 和 IDT 记录中指定的大小)。 TSS 限制用于确定 IOPB 大小。

我知道:

32-bit TSS structure can be visualized这样:

link还包含16位TSS和64位TSS结构的布局。


问题:

这是一个非常公平的问题。尽管乍一看带有或不带有 IO 端口位图 (IOPB) 的 TSS 本质上似乎相当微不足道,但它一直是激烈讨论的焦点;辩论;文件不正确;模棱两可的文件;以及来自 CPU 设计师的信息,这些信息有时会混淆水域。在 OS/2 Museum 中可以找到关于这个主题的非常好的读物。尽管名称如此,但信息并不限于 OS/2。摘自那篇总结它的文章:

It is obviously not trivial to use the IOPB correctly. In addition, an incorrectly set up IOPB is unlikely to cause obvious problems, but may disallow access to desired ports or (much worse, security-wise) allow access to undesired ports.

TSS 和 IOPB 的肮脏历史,因为它与 386BSD、NetBSD、OpenBSD 中的安全漏洞和错误有关,值得一读,如果您想避免引入,应该表明您提出的问题是合理的错误。


问题的答案

如果您不想要 IOPB,您可以简单地用整个 TSS 结构的长度填充 IOPB 偏移字段(不要减去 1)。您的 TSS 结构中不应有尾随 0xff 字节。 TSS 描述符中的 TSS 限制(如您所知)将比该值小 1。 Intel手册上说如果IOPB偏移值中的值大于TSS限制就没有IOPB。如果 IOPB 偏移字段中的值始终比限制大 1,则满足此条件。这就是现代 Microsoft Windows 处理它的方式。

如果使用 IOPB,请根据英特尔文档在末尾设置一个附加字节 0xff。通过为所有 0xff 设置一个额外的字节将阻止任何多端口访问 (INW/OUTW/INL/OUTL) 从最后 8 个端口开始或结束。这将避免多端口 read/write 可能跨越 IOPB 的末端导致访问超出 IOPB 范围的端口的情况。它还会拒绝从最后 8 个端口之前的端口开始并进入后面 8 个端口的多端口访问。如果多端口访问的 任何 端口的权限位设置为 1,则拒绝整个端口访问(根据 Intel 文档)

不清楚 x 在图表的上下文中代表什么,但如果这些位设置为 0,它们将显示为允许的端口,这不是您想要的。同样,坚持使用 Intel 文档并将额外的尾随字节设置为 0xff(所有位都设置为拒绝访问)。

来自Intel386 DX Microprocessor Data Sheet:

Each bit in the I/O Permission Bitmap corresponds to a single byte-wide I/O port, as illustrated in Figure 4-15a. If a bit is 0, I/O to the corresponding byte-wide port can occur without generating an exception. Otherwise the I/O instruction causes an exception 13 fault. Since every byte-wide I/O port must be protectable, all bits corresponding to a word-wide or dword-wide port must be 0 for the word-wide or dword-wide I/O to be permitted. If all the referenced bits are 0, the I/O will be allowed. If any referenced bits are 1, the attempted I/O will cause an exception 13 fault.

**IMPORTANT IMPLEMENTATION NOTE: Beyond the last byte of I/O mapping information in the I/O Permission Bitmap must be a byte containing all 1’s. The byte of all 1’s must be within the limit of the Intel386 DX TSS segment (see Figure 4-15a).


在 NASM 程序集中,您可以创建如下所示的结构:

tss_entry:
.back_link: dd 0
.esp0:      dd 0              ; Kernel stack pointer used on ring transitions
.ss0:       dd 0              ; Kernel stack segment used on ring transitions
.esp1:      dd 0
.ss1:       dd 0
.esp2:      dd 0
.ss2:       dd 0
.cr3:       dd 0
.eip:       dd 0
.eflags:    dd 0
.eax:       dd 0
.ecx:       dd 0
.edx:       dd 0
.ebx:       dd 0
.esp:       dd 0
.ebp:       dd 0
.esi:       dd 0
.edi:       dd 0
.es:        dd 0
.cs:        dd 0
.ss:        dd 0
.ds:        dd 0
.fs:        dd 0
.gs:        dd 0
.ldt:       dd 0
.trap:      dw 0
.iomap_base:dw TSS_SIZE         ; IOPB offset
;.cetssp:    dd 0              ; Need this if CET is enabled

; Insert any kernel defined task instance data here
; ...

; If using VME (Virtual Mode extensions) there need to bean additional 32 bytes
; available immediately preceding iomap. If using VME uncomment next 2 lines
;.vmeintmap:                     ; If VME enabled uncomment this line and the next
;TIMES 32    db 0                ;     32*8 bits = 256 bits (one bit for each interrupt)

.iomap:
TIMES TSS_IO_BITMAP_SIZE db 0x0
                                ; IO bitmap (IOPB) size 8192 (8*8192=65536) representing
                                ; all ports. An IO bitmap size of 0 would fault all IO
                                ; port access if IOPL < CPL (CPL=3 with v8086)
%if TSS_IO_BITMAP_SIZE > 0
.iomap_pad: db 0xff             ; Padding byte that has to be filled with 0xff
                                ; To deal with issues on some CPUs when using an IOPB
%endif
TSS_SIZE EQU $-tss_entry

特别说明:

  • 如果您使用高级语言并创建 TSS 结构,请确保使用压缩结构(即:使用 GCC 的 __attribute__((packed)) 或 MSVC 的 #pragma pack)。查看您的编译器文档以获取更多详细信息。不听从这个建议可能会导致额外的字节被添加到您的 TSS 结构的末尾,如果您有 IOPB,这可能会导致问题。如果 IOPB 存在于 TSS 中并且添加了额外的填充字节,那么这些字节将成为 IO 位图的一部分,并且可能 grant/deny 您不想要的权限。这是在 BSD 内核中产生错误的失败之一。
  • 在创建带或不带 IOPB 的 TSS 时,64 位 TSS 的规则是相同的。即使在长模式(64 位和兼容模式)下,64 位 TSS 仍然被使用,并且通过 LTR 指令加载到任务寄存器中,其方式与在传统保护模式下完成的方式相同。