Linux 内核 x86 架构:AMD 内存加密(SME/SEV/SNP/RMP/SVSM)深度解析
Linux 内核 x86 架构AMD 内存加密SME/SEV/SNP/RMP/SVSM深度解析【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本文围绕 Linux 内核仓库中的 AMD Memory Encryption 文档 展开系统讲解 AMD 处理器 Secure Memory EncryptionSME、Secure Encrypted VirtualizationSEV、Secure Nested PagingSNP、Reverse Map TableRMP与 Secure VM Service ModuleSVSM的硬件机制、CPUID/MSR 接口以及内核中的落地方式。读完本篇你将能够理解内核如何通过页表加密位保护 DRAM、如何在启动参数上启用mem_encrypton并在源码层面定位到 AMD 内存加密实现 与 SEV 来宾机密计算支持 的具体代码位置。一、SME 与 SEV 是什么SME 和 SEV 是 AMD 处理器提供的两项内存保护特性SMESecure Memory Encryption通过标准 x86 页表将单个物理内存页标记为加密。被标记的页在读出 DRAM 时由硬件自动解密、写回 DRAM 时自动加密因此可以防御针对系统 DRAM 的物理攻击。SEVSecure Encrypted Virtualization允许运行加密虚拟机。客户机guest的代码与数据被保护解密后的版本只在 VM 内部可见。SEV guest 区分私有内存private用 guest 专属密钥加密与共享内存shared可用 hypervisor 密钥加密当 SME 开启时hypervisor 密钥就是 SME 使用的密钥。内核中对应的 Kconfig 依赖关系可以印证这两项特性是一体的config X86_MEM_ENCRYPT select ARCH_HAS_FORCE_DMA_UNENCRYPTED select DYNAMIC_PHYSICAL_MASK def_bool n config AMD_MEM_ENCRYPT bool AMD Secure Memory Encryption (SME) support depends on X86_64 CPU_SUP_AMD depends on EFI_STUB select DMA_COHERENT_POOL select ARCH_USE_MEMREMAP_PROT select INSTRUCTION_DECODER select ARCH_HAS_CC_PLATFORM select X86_MEM_ENCRYPT select UNACCEPTED_MEMORY select CRYPTO_LIB_AES_GCM见 arch/x86/Kconfig从源码结构看SME 支持仅在 64 位、AMD CPU 且带 EFI_STUB 时可选并顺带选择了ARCH_HAS_CC_PLATFORM机密计算平台抽象说明内核把 SME/SEV 统一纳入了confidential computingCC框架。二、加密位CBit的工作机制页的加密由页表项中的加密位encryption bit控制其位置不是固定的需要通过 CPUID 查询见下文0x8000001f[ebx]。加密位的使用有两条路径加密位可以直接写进页表项PTE/PDE 等加密对应的内存页加密位也可以设置在CR3寄存器中从而使 PGD页全局目录本身被加密。进一步地页表的每一级都可以被加密只要指向下一级表的页表项中设置了加密位那一级页表就会被加密。这样整个页表层级full page table hierarchy都可以被加密。文档特别强调了一个易错点CR3 里设置了加密位并不意味着整个层级都被加密。例如 CR3 设了加密位、PGD 被加密但 PGD 中指向某个 PUD 的表项若未设加密位则该 PUD 表不会被加密。每一级页表项都要独立设置加密位才能达到全层级加密。SEV 场景下的硬件行为有三条规则SEV 开启时指令页instruction pages和 guest 页表始终被视为私有guest 内所有 DMA 操作必须落在共享内存上内核 Kconfig 中ARCH_HAS_FORCE_DMA_UNENCRYPTED正是为此服务强制 DMA 缓冲走未加密的共享区域加密位本身在 64 位或 32 位 PAE 模式下由 guest OS 控制在其它寻址模式下SEV 硬件会强制把内存加密位置 1。内核中加密位的位掩码mask由 arch/x86/mm/mem_encrypt_amd.c 在启动时初始化页表操作通过arch/x86/include/asm/mem_encrypt.h提供的宏如set_memory_encrypted等应用该掩码mem_encrypton这一启动参数则早在压缩启动阶段就被解析见 arch/x86/boot/compressed/misc.c 中的cmdline_find_option_bool(mem_encrypton)。此外arch/x86/kernel/sev_verify_cbit.S 提供内联汇编原语用于在 SEV 来宾中校验加密位是否按预期生效。三、CPUID 与 MSR 接口3.1 CPUID 0x8000001f特性探测通过 CPUID 指令判断 CPU 对 SME/SEV 的支持0x8000001f[eax]: Bit[0] 支持 SME Bit[1] 支持 SEV 0x8000001f[ebx]: Bits[5:0] 页表中用于激活内存加密的位编号即 CBit 位置 Bits[11:6] 启用内存加密后物理地址空间的缩减位数 只影响系统物理地址不影响 guest 物理地址同一 eax 中后续还有Bit[4]支持 SEV-SNP连续 RMP 的前提Bit[23]支持分段 RMPsegmented RMP。3.2 MSR 0xc0010010MSR_AMD64_SYSCFGSME 使能位若 CPU 支持 SMEMSR0xc0010010MSR_AMD64_SYSCFG用于查询/使能内存加密0xc0010010: Bit[23] 0 内存加密特性禁用 1 内存加密特性启用Linux 依赖 BIOS 来设置这一位BIOS 需要判断启用加密导致的物理地址空间缩减由 CPUID 报告的缩减位数决定与系统地址空间资源需求不冲突后才会置位。如果 Linux 启动时该位未设置Linux 自身不会去置位内存加密也就无法启用。这是部署 SME 时最常见的前提限制。3.3 MSR 0xc0010131MSR_AMD64_SEVSEV 激活状态若 CPU 支持 SEVMSR0xc0010131MSR_AMD64_SEV指示 SEV 是否激活0xc0010131: Bit[0] 0 内存加密未激活 1 内存加密已激活3.4 SME 在 Linux 中的三态模型文档把 Linux 内核中 SME 的状态归纳为三级排查问题时值得按此顺序核对状态定义SupportedCPU 支持 SME由 CPUID 指令确认EnabledSupported 且 MSR_AMD64_SYSCFG 的 bit 23 已置位ActiveSupported Enabled且 Linux 内核正在把加密位应用到页表项上内核中的 SME mask 非零四、启用 SMEmem_encrypton与 BIOS 的配合SME 可以在 BIOS 中启用enable并激活activate。两种情形效果不同BIOS 中同时启用并激活所有内存访问都会被加密无需再激活 Linux 的内存加密支持BIOS 仅启用只置 SYSCFG 的 bit 23此时可通过内核命令行参数mem_encrypton启用内存加密。关键限制再次强调如果 BIOS 根本没有启用 SMELinux 无法激活内存加密——即使内核默认配置为启用、或显式传了mem_encrypton。因此mem_encrypton只在 Supported Enabled 的前提下才有意义它只是把 SME 推进到 Active 状态的手段。启动流程上的源码落点mem_encrypton先被压缩内核解析arch/x86/boot/compressed/misc.c随后arch/x86/mm/Makefile在CONFIG_AMD_MEM_ENCRYPT下编译mem_encrypt_amd.o与mem_encrypt_boot.o见 arch/x86/mm/Makefilearch/x86/mm/mem_encrypt.c 中的mem_encrypt_init()与mem_encrypt_setup_arch()完成特性信息打印与启动期设置。五、SEV-SNP 与 guest 侧特性协商SEV-SNP 引入了一组新特性SEV_FEATURES[1:63]可由 hypervisor 为增强安全性而启用其中一些特性需要 guest 侧有对应实现才能正确工作。文档给出了 guest 启动行为随两侧支持情况变化的完整矩阵Hypervisor 启用特性Guest 需要实现Guest 有实现Guest 启动行为NoNoNo正常启动BootNoYesNo正常启动BootNoYesYes正常启动BootYesNoNo带特性启用启动Boot with feature enabledYesYesNo优雅启动失败Graceful boot failureYesYesYes带特性启用启动Boot with feature enabled更多细节见 AMD64 APM Vol 2 第 15.34.10 节SEV_STATUS MSR。可以推断只有当hypervisor 启用了特性、guest 声明需要实现、但 guest 实际没有实现时才会出现优雅失败这正是防止特性被静默降级的安全设计。六、Reverse Map TableRMPRMP 是位于系统内存中的结构用于保证系统物理地址SPA与 guest 物理地址GPA之间的一对一映射每个可能分配给 guest 的内存页在 RMP 中都有一个表项。RMP 表在内存中可以是连续的也可以是分段segmented的。6.1 连续 RMPContiguous RMP连续 RMP 的支持以 SEV-SNP 支持为前提CPUID0x8000001f[eax] Bit[4]。RMP 的位置通过两个 MSR 告知硬件0xc0010132 (RMP_BASE): RMP 首字节的系统物理地址 0xc0010133 (RMP_END): RMP 末字节的系统物理地址对齐要求硬件要求RMP_BASE与(RMP_END 1)8KB 对齐而 SEV 固件把要求提高到1MB 对齐。结构布局与容量计算RMP 由 16KB 的处理器记账区bookkeeping加上 RMP 表项组成每个表项 16 字节。RMP 的大小决定了 hypervisor 可分配给 SEV-SNP guest 的物理内存范围其覆盖的系统物理地址区间为0 到 ((RMP_END 1 - RMP_BASE - 16KB) / 16B) x 4KBLinux 侧的现状依赖 BIOS 为 RMP 分配/预留内存并正确设置RMP_BASE、RMP_ENDLinux 依据 MSR 值定位 RMP 并计算其大小。只有当 RMP 覆盖全部系统内存时Linux 才会启用 SEV-SNP。6.2 分段 RMPSegmented RMP分段 RMP 是 RMP 布局的新表示方式。早期实现要求 RMP 表在内存中连续而从远端 NUMA 节点访问 RMP 比从 RMP 所在节点访问更慢。分段 RMP 允许把 RMP 表项放在其覆盖内存所在的同一节点上从而降低访问 RMP 表项的延迟。每个 RMP 段覆盖一段特定的系统物理地址范围。能力探测CPUID0x8000001f[eax]: Bit[23] 支持分段 RMP 若支持段属性见: 0x80000025[eax]: Bits[5:0] 支持的最小 RMP 段大小 Bits[11:6] 支持的最大 RMP 段大小 0x80000025[ebx]: Bits[9:0] 可缓存cacheableRMP 段定义的数量 Bit[10] 该可缓存段数量是否为硬性上限启用分段 RMP 使用新 MSR0xc0010136 (RMP_CFG): Bit[0] 分段 RMP 是否启用 Bits[13:8] 每个 RMP 段覆盖的内存大小2 的幂指数RMP_CFG中的段大小适用于 RMP 的所有段。文档给出了一个具体例子若RMP_CFG 0x2401则段覆盖值为0x24即 36每个段覆盖 64GB1 36内存。于是第一个段覆盖物理地址0到0xF_FFFF_FFFF第二个段覆盖0x10_0000_0000到0x1F_FFFF_FFFF依此类推。布局上启用分段 RMP 后RMP_BASE仍指向 16K 的记账区但记账区之后不再是紧跟的 RMP 表项而是一个 4K 的 RMP 段表RSTRMP Segment Table。RST 每个表项 8 字节表示一个 RMP 段Bits[19:0] 映射大小单位 GB可以小于定义的段大小。 为 0 表示该段对应的系统物理地址范围不存在 RMP。 Bits[51:20] 段的物理地址左移 20 位或读取时掩码 即得段的物理地址1MB 对齐。RST 最多容纳 512 个段表项但若 CPUID0x80000025_EBX[10]指示可缓存段数量是硬上限则 RST 可被限制为0x80000025_EBX[9:0]所给出的数量。与连续 RMP 相同Linux 的分段 RMP 支持也依赖 BIOS 完成内存分配/预留记账区、RST 与所有段、构建 RST并正确设置RMP_BASE、RMP_END、RMP_CFGLinux 依据 MSR 值定位 RMP 段并计算大小与位置且 RMP 必须覆盖全部系统内存Linux 才会启用 SEV-SNP。更多细节见 AMD64 APM Vol 2 15.36.3 Reverse Map Table 一节docID: 24593。七、SVSMSecure VM Service ModuleSNP 提供了虚拟机特权级VMPLVirtual Machine Privilege Levels特性定义 guest 软件可运行的四个特权级数字越小特权越高最高特权级为 0。不同服务可以运行在不同的保护级别上——位于 guest OS 之外、但仍在安全的 SNP 环境内——为 guest 提供例如 vTPM 之类的服务。当 guest 不运行在 VMPL0 时它需要与 VMPL0 上运行的软件通信才能执行特权操作或访问安全服务。典型例子是PVALIDATE指令——它必须在 VMPL0 执行。在这种场景下运行在 VMPL0 的软件通常被称为SVSMSecure VM Service Module。SVSM 的发现discovery机制及其通信 API 在 AMD 文档 Secure VM Service Module for SEV-SNP GuestsdocID: 58019中有定义VMPL 的细节见 AMD64 APM Vol 2 15.35.7 Virtual Machine Privilege LevelsdocID: 24593。Linux 内核中已有对应的实现落点arch/x86/coco/sev/svsm.c 实现了 guest 与 SVSM 的通信协议。从源码结构看guest 通过 GHCBGuest-Hypervisor Communication Block发起SVM_VMGEXIT_SNP_RUN_VMPL类型的运行请求svsm_perform_ghcb_protocol()中可见ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_SNP_RUN_VMPL)并为早期启动准备了身份映射的 CAACommunication Access Area页面boot_svsm_ca_page以同时支持引导早期和常规内核虚拟地址两种场景。SEV 来宾的其余机密计算组件VC 共享内存处理、core 初始化等集中在 arch/x86/coco/sev/ 目录下core.c、vc-shared.c、vc-handle.c等通用 CC 平台抽象入口在 arch/x86/coco/core.c。八、部署核对清单综合文档与源码在 AMD 平台上核对内存加密状态时可按以下顺序操作均基于当前仓库文档与实现确认 BIOS 状态检查 SYSCFG MSR0xc0010010bit 23 是否已置位若 BIOS 已启用激活内核无需额外操作。配置内核确保CONFIG_AMD_MEM_ENCRYPTy依赖X86_64、CPU_SUP_AMD、EFI_STUB见 arch/x86/Kconfig。如需内核激活传递启动参数mem_encrypton仅在 BIOS 已启用但未激活 SME 时生效。按三态模型核对SupportedCPUID0x8000001f[eax] Bit[0]→ EnabledSYSCFG bit 23→ Active内核 SME mask 非零页表项实际应用加密位。SEV 来宾场景确认 MSR0xc0010131bit 0 表示加密激活SNP 场景再确认 RMP 是否覆盖全部系统内存连续 RMP 查RMP_BASE/RMP_END分段 RMP 另查RMP_CFG以及 guest 侧特性矩阵是否落入正常启动或带特性启动行。需要牢记的前提与限制Linux 从不自行设置 SYSCFG 的 SME 位RMP无论连续还是分段的内存预留与 MSR 设置均由 BIOS 负责guest 内 DMA 必须使用共享内存。这些约束决定了整个方案中 BIOS 固件与固件-内核分工是能否跑通的关键。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考