Zircon 内核不变量

在 x86 上,Zircon 需要为在 Ring 0(内核模式)中运行的代码保持以下不变性。

这里记录了这些不变特性,因为它们不一定易于测试;Zircon 的测试套件不会捕获到破坏不变变异。

  • 标记会注册:

    • 方向标记 (DF) 应为 0。这是 x86 调用规范的要求。

    如果将此标志设置为 1,使用 x86 字符串指令(例如,在 memcpy() 中使用 rep movs 或由编译器内联)可能会出错,并且复制方向错误。函数可以将此标志暂时设置为 1,前提是在返回或调用其他函数之前将其改回 0。

    • 对齐检查标志 (AC) 通常应为 0。在支持 SMAP 的 CPU 上,这样可以防止内核意外读取或写入用户空间数据。
  • 每当在内核模式下运行并启用中断时,gs_base 寄存器都必须指向当前 CPU 的 x86_percpu 结构体。只有在停用中断时,才能将 gs_base 更改为指向其他内容。例如,swapgs 指令应仅在停用中断时使用。

  • 以下各项由编译器部分强制执行:

    • 不允许使用扩展寄存器(SSE、AVX、x87 等),因为这会破坏用户区的注册状态。

    这部分是通过将 -mno-sse 传递给编译器来实现的。要防止编译器在优化(例如内存副本)中使用 SSE 寄存器,必须使用此选项。

    我们希望防止在内核代码中意外使用 floatdouble 类型,但 GCC 和 Clang 无法在所有情况下都能做到这一点。-mno-sse 不会阻止将 float/double 与任一编译器一起使用;编译器将改用 x87 指令。

    我们使用 -msoft-float 进行编译,这似乎可以阻止 GCC 生成 x87 指令(并因此使用 x87 寄存器):GCC 6.3.0 会针对 float/double 算术和返回值发出错误,但不会阻止这些类型作为参数传递。但是,将 -msoft-float 传递给 Clang 似乎没有任何作用:Clang 7.0.0 仍会为使用 floatdouble 的代码生成 x87 指令(并使用 x87 寄存器)。

    • 没有将数据存储在堆栈中的 %rsp 以下。请注意,userland 代码可以做到这一点:SysV x86-64 ABI 允许函数将数据存储在“红色区域”(即 %rsp 以下 128 个字节)中。不过,内核代码无法使用红色区域,因为中断可能会破坏此区域 - CPU 在调用中断处理程序时,会将数据推送到 %rsp 正下方的堆栈。

    这通常是通过将 -mno-red-zone 传递给编译器来实现的。