Arm64 Linux MTE 使能

在Arm Base FVP平台上,linux的默认配置是打开了MTE的,也就是:

CONFIG_ARM64_AS_HAS_MTE=y
CONFIG_ARM64_MTE=y

当运行Base FVP的时候加上如下参数:

-C cluster0.memory_tagging_support_level=4   \
-C cluster1.memory_tagging_support_level=4   \

这个时候运行Linux kernel的时候会出现如下错误:

[    0.000000] CPU features: detected: Virtualization Host Extensions
[    0.000000] CPU features: detected: Memory Tagging Extension
[    0.000000] CPU features: detected: Asymmetric MTE Tag Check Fault
[    0.000000] Internal error: Oops - Undefined instruction: 0000000002000000 [#1] PREEMPT SMP
[    0.000000] Modules linked in:
[    0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 6.10.0-rc1-00013-g2bfcfd584ff5 #2
[    0.000000] Hardware name: FVP Base RevC (DT)
[    0.000000] pstate: 200000c9 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[    0.000000] pc : mte_cpu_setup+0x2c/0x74
[    0.000000] lr : cpu_enable_mte+0x30/0x14c
[    0.000000] sp : ffff800082623db0
[    0.000000] x29: ffff800082623db0 x28: 00000000fe6d4c40 x27: 0000000000000000
[    0.000000] x26: ffff800081b8ff30 x25: ffff800082629b40 x24: ffff800082ae3000
[    0.000000] x23: 0000000000000004 x22: ffff800082ae3250 x21: ffff800081b92000
[    0.000000] x20: ffff800081b91458 x19: ffff800081b912f8 x18: 0000000000000006
[    0.000000] x17: 687469726f676c61 x16: 2046454420504d49 x15: ffff800082623800
[    0.000000] x14: 0000000000000000 x13: ffff800082641f90 x12: 0000000000000072
[    0.000000] x11: 0000000000000026 x10: ffff800082699f90 x9 : ffff800082641f90
[    0.000000] x8 : 00000000ffffefff x7 : 000000000000003c x6 : 000000000000000f
[    0.000000] x5 : 00000000410fd0f0 x4 : 0000000000000000 x3 : 0000000000000001
[    0.000000] x2 : 0314000040000321 x1 : 000000040044ffff x0 : 000000000001ffff
[    0.000000] Call trace:
[    0.000000]  mte_cpu_setup+0x2c/0x74
[    0.000000]  enable_cpu_capabilities+0x90/0xe4
[    0.000000]  setup_boot_cpu_features+0x34/0x44
[    0.000000]  smp_prepare_boot_cpu+0x34/0x40
[    0.000000]  start_kernel+0x1a4/0x708
[    0.000000]  __primary_switched+0x80/0x88
[    0.000000] Code: eb00003f 54000040 d518a200 b24043e0 (d51810c0)
[    0.000000] ---[ end trace 0000000000000000 ]---
[    0.000000] Kernel panic - not syncing: Attempted to kill the idle task!
[    0.000000] ---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]---

这里出现这个错误的原因是写寄存器 GCR_EL1的时候出现了Undefined instruction。 对应的代码和汇编:

void mte_cpu_setup(void)
{
        //...
        write_sysreg_s(KERNEL_GCR_EL1, SYS_GCR_EL1);
mte_cpu_setup:
EL2N:0xFFFF800080031210 : MRS      x0,TTBR0_EL1
EL2N:0xFFFF800080031214 : TBNZ     w0,#0,{pc}+104 ; 0xFFFF80008003127C
EL2N:0xFFFF800080031218 : MRS      x0,TTBR1_EL1
EL2N:0xFFFF80008003121C : TBNZ     w0,#0,{pc}+100 ; 0xFFFF800080031280
EL2N:0xFFFF800080031220 : MRS      x1,MAIR_EL1
EL2N:0xFFFF800080031224 : AND      x0,x1,#0xffffffffffff00ff
EL2N:0xFFFF800080031228 : ORR      x0,x0,#0xf000
EL2N:0xFFFF80008003122C : CMP      x1,x0
EL2N:0xFFFF800080031230 : B.EQ     {pc}+8 ; 0xFFFF800080031238
EL2N:0xFFFF800080031234 : MSR      MAIR_EL1,x0
EL2N:0xFFFF800080031238 : ORR      x0,xzr,#0x1ffff
EL2N:0xFFFF80008003123C : MSR      GCR_EL1,x0

根据Arm Arm D23.2.47的定义:

//....
elsif PSTATE.EL == EL2 then
        if HaveEL(EL3) && EL3SDDUndefPriority() && SCR_EL3.ATA == '0' then
                UNDEFINED;
        elsif HaveEL(EL3) && SCR_EL3.ATA == '0' then
                if EL3SDDUndef() then
                        UNDEFINED;
                else
                        AArch64.SystemAccessTrap(EL3, 0x18);
        else
                GCR_EL1 = X[t, 64];

再在armdbg里检查一下SCR_EL3.ATA

>p/t $SCR_EL3
$3 = 110000011100110001
>p/t $SCR_EL3 >> 26
$4 = 0

基本上可以确定在TF-A里面没有使能这个ATA bit, 修改TF-A的编译参数,在编译时加上 ENABLE_FEAT_MTE2=1 :

make PLAT=fvp DEBUG=1 BL33=$(SRC_DIR)/u-boot/u-boot.bin all fip V=1 ENABLE_FEAT_MTE2=1

再检查SCR_EL3的值, Linux就可以正常boot了。

>p/t $SCR_EL3
$4 = 100000000110000011100110001
>p/t $SCR_EL3 >>25
$10 = 10

小技巧

如果想在boot阶段临时把MTE feature 给临时关闭掉,可以在bootargs里面加上参数arm64.nomte:

u-boot 里面修改:

CONFIG_BOOTARGS="console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda1 rw ip=dhcp debug user_debug=31 loglevel=9 arm64.nomte "

u-boot 启动阶段临时修改:

bootargs=console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda1 rw ip=dhcp debug user_debug=31 loglevel=9

参考代码 arch/arm64/kernel/pi/idreg-override.c:

static const struct {
        char    alias[FTR_ALIAS_NAME_LEN];
        char    feature[FTR_ALIAS_OPTION_LEN];
} aliases[] __initconst = {
        { "kvm_arm.mode=nvhe",          "id_aa64mmfr1.vh=0" },
        { "kvm_arm.mode=protected",     "id_aa64mmfr1.vh=0" },
        { "arm64.nosve",                "id_aa64pfr0.sve=0" },
        { "arm64.nosme",                "id_aa64pfr1.sme=0" },
        { "arm64.nobti",                "id_aa64pfr1.bt=0" },
        { "arm64.nopauth",
          "id_aa64isar1.gpi=0 id_aa64isar1.gpa=0 "
          "id_aa64isar1.api=0 id_aa64isar1.apa=0 "
          "id_aa64isar2.gpa3=0 id_aa64isar2.apa3=0"        },
        { "arm64.nomops",               "id_aa64isar2.mops=0" },
        { "arm64.nomte",                "id_aa64pfr1.mte=0" },
        { "nokaslr",                    "arm64_sw.nokaslr=1" },
        { "rodata=off",                 "arm64_sw.rodataoff=1" },
        { "arm64.nolva",                "id_aa64mmfr2.varange=0" },
};

Comments !