啥都吃的豆芽 · 2023年03月23日 · 北京市

优化原理 - E0PD

E0PD是ARMv8.5扩展引入的一个硬件防护特性,它用来替代KPTI避免来自用户态的Meltdown漏洞攻击。KPTI技术通过在返回用户态时unmap kernel space的方式避免内核地址空间的暴露,因此在退出内核态时unmap内核页表以及在进入内核态时重新映射内核页表会带来极大性能开销。E0PD在硬件侧防护内核地址空间从而使内核可以绕过KPTI,因而带来性能提升。

在内核优化方面,开启E0PD绕过KPTI的代码如下(省略一些无关的代码片段)。在boot阶段,内核调用unmap_kernel_at_el0函数检测是否需要开启KPTI unmap内核地址空间功能,其中的重要一项检测就是调用kaslr\_requires_kpti,在此函数内检查硬件是否支持E0PD并且内核是否打开E0PD,若是则返回false,表示不需要启用KPTI功能。

static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */

static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
                int scope)
{
    …………

    /* Useful for KASLR robustness */
    if (kaslr_requires_kpti()) {
        if (!__kpti_forced) {
            str = "KASLR";
            __kpti_forced = 1;
        }
    }

  …………

    /* Forced? */
    if (__kpti_forced) {
        pr_info_once("kernel page table isolation forced %s by %s\n",
                 __kpti_forced > 0 ? "ON" : "OFF", str);
        return __kpti_forced > 0;
    }

    …………
}
/*
 * This check is triggered during the early boot before the cpufeature
 * is initialised. Checking the status on the local CPU allows the boot
 * CPU to detect the need for non-global mappings and thus avoiding a
 * pagetable re-write after all the CPUs are booted. This check will be
 * anyway run on individual CPUs, allowing us to get the consistent
 * state once the SMP CPUs are up and thus make the switch to non-global
 * mappings if required.
 */
bool kaslr_requires_kpti(void)
{
    if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE))
        return false;

    /*
     * E0PD does a similar job to KPTI so can be used instead
     * where available.
     */
    if (IS_ENABLED(CONFIG_ARM64_E0PD)) {
        u64 mmfr2 = read_sysreg_s(SYS_ID_AA64MMFR2_EL1);
        if (cpuid_feature_extract_unsigned_field(mmfr2,
                        ID_AA64MMFR2_E0PD_SHIFT))
            return false;
    }

    /*
     * Systems affected by Cavium erratum 24756 are incompatible
     * with KPTI.
     */
    if (IS_ENABLED(CONFIG_CAVIUM_ERRATUM_27456)) {
        extern const struct midr_range cavium_erratum_27456_cpus[];

        if (is_midr_in_range_list(read_cpuid_id(),
                      cavium_erratum_27456_cpus))
            return false;
    }

    return kaslr_offset() > 0;
}

使用方法

使用Alinux3.2208及以后版本

Alinux3在2208版本(内核版本5.10.134)已默认启用 说明:从upstream打上E0PD相关patch(anck 5.10已经合入),

  • 3e6c69a arm64: Add initial support for E0PD
  • 92ac6fd arm64: Don't use KPTI where we have E0PD

编译内核时启用E0PD特性

如果需要自己编译内核,可以在编译参数中打开E0PD配置

CONFIG_ARM64_E0PD=y

性能收益

KPTI的内核地址空间map、unmap操作在通用路径上,所以E0PD对KPTI的优化影响广泛。在我们的测试中,E0PD优化对基础benchmark和E2E应该都带来了5-23%的大幅度性能提升

文章来源:龙蜥社区

推荐阅读
倚天710性能系列

更多Arm服务器相关技术及移植干货请关注Arm服务器专栏。如要加入Arm Server微信群,请添加极术小姐姐(微信id:aijishu20)备注Arm服务器邀请加入。
推荐阅读
关注数
17328
内容数
73
分享arm服务器软件应用经验、测试方法、优化思路、工具使用等。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息