为什么 x86 分页没有权限环的概念?

问题描述

早在 1982 年,当 Intel 发布 80286 时,他们为分段方案(环 0-3)添加了 4 个特权级别,由全局描述符表 (GDT) 和本地描述符表 (LDT) 中的 2 位指定。

在 80386 处理器中,Intel 添加分页,但令人惊讶的是,它只有 2 个权限级别(主管和用户),由页目录项 (PDE) 和页表项 (PTE) 中的单个位指定。

>

这意味着仅使用分页的操作系统(如大多数现代操作系统)无法从环 1 和环 2 的存在中受益,这对于驱动程序来说可能非常有用。 (例如,Win9x 经常崩溃,因为它正在将有问题的未经检查的驱动程序加载到 ring 0 中)。

从可移植性的角度来看,环 1 和环 2 的存在是 x86 架构的一个怪癖,可移植操作系统不应该使用它们,因为其他架构只有 2 个特权级别。

但我确信,英特尔工程师在 1985 年设计 386 时并没有想到可移植到其他平台。

那么为什么英特尔不允许分页有 4 个权限级别,比如分段?

解决方法

386保护模式和286一样有四个权限级别(称为ring):ring 0的权限最高(操作系统),ring 1和2的使用并不广泛,ring 3的权限最低(用户申请)。 0-2环称为“Supervisor”,环3称为“User”。

当前权限级别 (CPL) 由您正在执行的指令的地址决定,根据代码段的描述符权限级别 (DPL)。有关当前权限级别的详细信息,请参阅 CPL vs. DPL vs. RPL.

您所指的位如下。它是映射 4MB 页面(或引用页表的 32 位 PDE)的 32 位页目录条目 (PDE) 中的位 2。该位称为“用户/主管”(U/S)。此位中的值“0”表示不允许用户模式访问此条目控制的 4MB 区域。这并不意味着正如您所写的那样,只有“2 个权限级别(主管和用户)”。 “主管”级别仍然由三个环组成。这与用户环一起包括总共四个环。

请参阅 Intel 64 和 IA-32 架构软件开发人员手册,第 3A 卷:系统编程指南,第 1 部分的第 4.6 节:

对线性地址的每次访问要么是管理员模式访问,要么是 用户模式访问。对于所有指令提取和大多数数据 访问,这个区别是由当前权限决定的 级别 (CPL):在 CPL

因此,CPL 可以是 0、1、2 和 3,有效地拥有所有 4 个环。

请从上述手册中找到有关美国国旗的更多信息:

某些操作隐式访问系统数据结构 地址 [...] 称为隐式监督模式访问,无论 CPL。 CPL

附言我的回答没有解决为什么环 1 和环 0 之间没有与环 3 和环 0/1/2 之间相同的内存保护的问题,因此如果页表条目,环 1 和环 2 不可用无法将它们与环 0 区分开来。See the reply by Peter Cordes that addresses this issue.

,

我想到的一个猜测是,英特尔打算在运行环 1 代码时, 是主管,“监督”环 3 代码。不在环 0 下运行 环 1。

如果 ring 1 代码想要调用 ring 0 代码,它可以通过调用门调用,并且 ring 0 代码可以将 CR3 更改为页表,该页表包括页面中不存在的物理页面的映射表中使用的环 1 或 2 代码。

我真的不太了解这些东西,但是 https://wiki.osdev.org/Task_State_Segment 显示 TSS 包含一个 CR3 字段,因此使用硬件任务切换我猜测通过调用门调用可以触发CR3直接改。 (所以调用目标不必已经被映射,否则环 1 / 2 代码可能已经修改了它。或者它可以被映射为只读,连同页表本身和 GDT,通过修改来阻止环 1 代码接管环 0。)

这意味着仅使用分页的操作系统 [...] 无法从环 1 和环 2 的存在中受益

那是你的错误:你不能“只使用分页”。即使在普通的 x86 操作系统(具有平面内存模型)上进行用户空间的中断处理,也需要设置 TSS 内容,以便在切换到内核模式时将 ESP 设置为内核堆栈指针,即使您不使用硬件任务- 切换。

x86 有“任务门”和“调用门”以及各种非常复杂的东西,我希望我不必完全理解,但我希望花一些时间阅读它可能会对386 的架构师认为操作系统可能想做的事情。

与我之前的猜测不同(关于环 1 监督环 3),也许英特尔希望操作系统使用分段将环 1 / 2 与同一页表中的环 0 内存分开(如果需要)1。正如您所说,他们可能并没有尝试创建便携式微内核操作系统可以作为奖励使用的东西。

内核可以决定虚拟地址空间的布局,因此它可以很好地分配其中的块以供环 1 代码使用,并在调用时适当地设置 CS/DS/ES/SS。

不过,我认为这必须意味着非平面模型,因为 x86 分段使地址 go from 0..limit,而不是例如允许在不改变指针含义的情况下访问从低到高的一系列虚拟地址。

脚注 1

ring 0 和 ring 1 之间是否需要全内存保护?操作系统可能将环 1 用于半可信代码。

一些特权指令需要环 0,所以环 1 会阻止这种意外发生。 IO 权限级别可以单独设置以允许 cliin/out in ring > 0,但其他指令如 invlpglgdt 和 {{ 3}} 需要实际的环 0。

,

希望保护东西不受其他东西的影响。在分页出现之前(并且在 80x86 出现之前——“4 环”模型可以追溯到 Multics,如果不是更早的话)最简单的方法是使用“环”。

使用 4 个环,您可以有一个“D 不能访问 C,他们不能访问 B,他们都不能访问 A”的安排。这对于相反的方向来说相对糟糕(“C 可以访问 D 中的所有内容,而不管它是否需要”),并且粒度相对糟糕(例如,如果您希望“C 可以访问 D 的一部分但不是 D 的全部”) .

通过分页,您可以为每个事物提供自己的虚拟地址空间并将任何内容映射到任何地方以控制访问(因为您无法访问任何未映射到您的虚拟地址空间的内容)。您仍然可以通过将属于 D 的所有页面映射到 A、B 和C;并将属于 C 的所有页面映射到 A 和 B;等等。但是,您也可以有任何其他安排 - 例如模拟 10 个环而不是 4 个环,或者让 C 访问 D 的一部分(但不是 D 的全部)和 B 的一部分(但不是 B 的全部),或者...

问题就变成了;如果仅分页就足以模拟任意数量的环(甚至更多),为什么我们还有 2 个环?

答案是分页仅控制对内存中事物(代码、数据)的访问,而不能/无法控制对不在内存中的事物(例如 CPU 的控制寄存器)的访问。仍然需要 2 个环来控制是否可以/不能访问不在内存中的东西(例如,mov cr0,eax 指令是否会导致一般保护错误)。

然而;有两件事使这不那么明显。在不同的虚拟地址空间之间切换会产生一些与之相关的成本,人们试图将这种成本降至最低(例如,不给共享库提供自己单独的虚拟地址空间,不给单个设备驱动程序提供自己的虚拟地址空间等);并且因为分页被添加到预先存在的“4 环分割”设计碎片中(具有向后兼容性问题),旧的“4 环分割”仍然在使用(例如 TSS、IO 权限系统等)。