使用或不使用 IO 位图创建正确的任务状态段 (TSS) 结构?

Mic*_*tch 3 x86 x86-64 intel osdev amd-processor

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

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

我知道:

  • TSS描述符限制比整个TSS结构的大小小1
  • 16位TSS没有IOPB并且结构是固定大小的
  • 基本的32位和64位TSS结构大小相似(数据含义不同)
  • 32 位 TSS 可以通过向基本结构添加额外的 DWORD 来支持控制流强制。
  • TSS 中的 IOPB 偏移量(字)指向相对于任务段开头的偏移量。
  • IOPB 偏移量指向 IOPB 结构的开头,并且在启用虚拟模式增强 (VME)的情况下,IOPB 之前的 32 个字节是中断重定向表。
  • 如果未启用 VME,内核可以在基本 TSS 结构的末尾和 IOPB 偏移之间放置额外的每个任务实例数据
  • 如果启用了 VME,内核可以在基本 TSS 结构的末尾和 IOPB 以下 32 字节的偏移量之间放置额外的每个任务实例数据。
  • 如果存在 IOPB,则每个 0 位表示端口访问权限,1 位表示拒绝权限。

32 位 TSS 结构可以这样可视化:

在此输入图像描述

该链接还包含 16 位 TSS 和 64 位 TSS 结构的布局。


问题:

  • 如果我想要一个没有 IOPB 的 TSS,我应该为 +66h 处的 IOPB 偏移填写什么值?
  • 如果我想要带有 IOPB 的 TSS,是否必须0xff在 IOPB 末尾添加一个字节?
  • 在上图中,为什么末尾的额外字节被表示为xxxxx111. 如果最后一个字节应该是,0xff那不是吗11111111

Mic*_*tch 5

这是一个非常公平的问题。尽管乍一看,带或不带 IO 端口位图 (IOPB) 的 TSS 本质上似乎相当微不足道,但它一直是激烈讨论的焦点;辩论; 文件不正确;不明确的文档;CPU 设计者提供的信息有时会混淆视听。关于这个主题的很好的读物可以在OS/2 Museum中找到。尽管有这个名称,但该信息并不限于 OS/2。从那篇文章中总结出来的一个要点是:

\n\n
\n

正确使用 IOPB 显然并非易事。此外,错误设置的 IOPB 不太可能导致明显的问题,但可能不允许访问所需的端口,或者(更糟糕的是,从安全角度来看)允许访问不需要的端口。

\n
\n\n

TSS 和 IOPB 的肮脏历史与 386BSD、NetBSD、OpenBSD 中的安全漏洞和错误有关,读起来很有趣,并且应该表明,如果您希望避免引入错误,您提出的问题是合理的。

\n\n
\n\n

问题解答

\n\n

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

\n\n

0xff如果使用 IOPB,请根据 Intel 文档在末尾设置一个附加字节。通过为 all 设置一个额外字节,0xff可以防止在最后 8 个端口开始或结束的任何多端口访问 (INW/OUTW/INL/OUTL)。这将避免多端口读/写可能跨越 IOPB 的末端,从而导致对落在 IOPB 范围之外的端口进行访问的情况。它还会拒绝从最后 8 个端口之前的端口开始并交叉到后面 8 个端口的多端口访问。如果多端口访问的任何端口的权限位设置为 1,则整个端口访问将被拒绝(根据 Intel 文档)

\n\n

目前尚不清楚x图中的含义,但如果这些位设置为 0,它们将显示为允许的端口,这不是您想要的。再次强调,遵循英特尔文档并设置一个额外的尾随字节0xff(所有位设置为拒绝访问)。

\n\n

来自Intel386 DX 微处理器数据表

\n\n
\n

I/O 权限位图中的每一位对应一个字节宽的 I/O 端口,如图 4-15a 所示。如果某个位为 0,则可以对相应字节宽端口进行 I/O,而不会生成异常。否则,I/O 指令会导致异常 13 故障。由于每个字节宽 I/O 端口都必须是可保护的,因此与字宽或双字宽端口对应的所有位都必须为 0,才能允许字宽或双字宽 I/O。如果所有引用的位均为 0,则允许 I/O。如果任何引用位为 1,则尝试的 I/O 将导致异常 13 故障。

\n
\n\n

\n\n
\n

**重要实现注意事项:I/O 权限位图中 I/O 映射信息的最后一个字节之外必须是包含所有 1\xe2\x80\x99 的字节。所有 1\xe2\x80\x99 的字节必须在 Intel386 DX TSS 段的限制内(见图 4-15a)。

\n
\n\n
\n\n

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

\n\n
tss_entry:\n.back_link: dd 0\n.esp0:      dd 0              ; Kernel stack pointer used on ring transitions\n.ss0:       dd 0              ; Kernel stack segment used on ring transitions\n.esp1:      dd 0\n.ss1:       dd 0\n.esp2:      dd 0\n.ss2:       dd 0\n.cr3:       dd 0\n.eip:       dd 0\n.eflags:    dd 0\n.eax:       dd 0\n.ecx:       dd 0\n.edx:       dd 0\n.ebx:       dd 0\n.esp:       dd 0\n.ebp:       dd 0\n.esi:       dd 0\n.edi:       dd 0\n.es:        dd 0\n.cs:        dd 0\n.ss:        dd 0\n.ds:        dd 0\n.fs:        dd 0\n.gs:        dd 0\n.ldt:       dd 0\n.trap:      dw 0\n.iomap_base:dw TSS_SIZE         ; IOPB offset\n;.cetssp:    dd 0              ; Need this if CET is enabled\n\n; Insert any kernel defined task instance data here\n; ...\n\n; If using VME (Virtual Mode extensions) there need to bean additional 32 bytes\n; available immediately preceding iomap. If using VME uncomment next 2 lines\n;.vmeintmap:                     ; If VME enabled uncomment this line and the next\n;TIMES 32    db 0                ;     32*8 bits = 256 bits (one bit for each interrupt)\n\n.iomap:\nTIMES TSS_IO_BITMAP_SIZE db 0x0\n                                ; IO bitmap (IOPB) size 8192 (8*8192=65536) representing\n                                ; all ports. An IO bitmap size of 0 would fault all IO\n                                ; port access if IOPL < CPL (CPL=3 with v8086)\n%if TSS_IO_BITMAP_SIZE > 0\n.iomap_pad: db 0xff             ; Padding byte that has to be filled with 0xff\n                                ; To deal with issues on some CPUs when using an IOPB\n%endif\nTSS_SIZE EQU $-tss_entry\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

特别提示:

\n\n
    \n
  • 如果您使用高级语言并创建 TSS 结构,请确保使用打包结构(即:使用 GCC__attribute__((packed))或 MSVC #pragma pack)。查看您的编译器文档以获取更多详细信息。如果不注意此建议,可能会导致在 TSS 结构的末尾添加额外的字节,如果您有 IOPB,则可能会导致问题。如果 TSS 中存在 IOPB,并且添加了额外的填充字节,这些字节将成为 IO 位图的一部分,并可能授予/拒绝您不希望的权限。这是在 BSD 内核中产生错误的故障之一。
  • \n
  • 在创建带有或不带有 IOPB 的 TSS 时,64 位 TSS 的规则是相同的。即使在长模式(64 位和兼容模式)下,仍会使用 64 位 TSS,并将其加载到任务寄存器中,其方式与在传统保护模式下通过指令完成的操作相同LTR
  • \n
\n