明日にはでっかい太陽が昇るかもしれません。

「覚悟」とは!! 暗闇の荒野に!!進むべき道を切り開く事だッ!

Linux (aarch64) on Qemu を動作させたい (2)

Qemu への SEV 対応は、以下のような情報があり、 "パフォーマンスが悪いから nop にしているよ" みたいな感じに読めたけど、今回はパフォーマンスよりも動作の再現性を優先したいので、実装してみる。

Re: [Qemu-devel] implemetation of wfe and sev instructions on aarch64

diff --git a/target/arm/helper.h b/target/arm/helper.h
index df86bf71415..5cddcd0627f 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -50,6 +50,7 @@ DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32)
 DEF_HELPER_1(setend, void, env)
 DEF_HELPER_1(wfi, void, env)
 DEF_HELPER_1(wfe, void, env)
+DEF_HELPER_1(sev, void, env)
 DEF_HELPER_1(yield, void, env)
 DEF_HELPER_1(pre_hvc, void, env)
 DEF_HELPER_2(pre_smc, void, env, i32)
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index 2a856665797..dce0e11c011 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -445,6 +445,21 @@ void HELPER(wfe)(CPUARMState *env)
     HELPER(yield)(env);
 }
 
+void HELPER(sev)(CPUARMState *env)
+{
+    ARMCPU *cpu = arm_env_get_cpu(env);
+    CPUState *cs0 = CPU(cpu), *cs = cs0;
+
+    CPU_FOREACH(cs) {
+        if (cs->halted) {
+            fprintf(stderr, "%s: cpu%d halted (%p)\n", __func__, cs->cpu_index, cs->halt_cond);
+            qemu_cond_signal(cs->halt_cond);
+        }
+    }
+    //cs0->exception_index = EXCP_INTERRUPT;
+    cpu_loop_exit(cs0);
+}
+
 void HELPER(yield)(CPUARMState *env)
 {
     ARMCPU *cpu = arm_env_get_cpu(env);
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index cb44632d16a..d2b937a4343 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -1345,7 +1345,7 @@ static void handle_hint(DisasContext *s, uint32_t insn,
         return;
     case 4: /* SEV */
     case 5: /* SEVL */
-        /* we treat all as NOP at least for now */
+        s->is_jmp = DISAS_SEV;
         return;
     default:
         /* default specified as NOP equivalent */
@@ -11385,6 +11385,10 @@ void gen_intermediate_code_a64(CPUState *cs, TranslationBlock *tb)
             gen_a64_set_pc_im(dc->pc);
             gen_helper_wfe(cpu_env);
             break;
+        case DISAS_SEV:
+            gen_a64_set_pc_im(dc->pc);
+            gen_helper_sev(cpu_env);
+            break;
         case DISAS_YIELD:
             gen_a64_set_pc_im(dc->pc);
             gen_helper_yield(cpu_env);
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 2fe144baa9b..c936488cdc3 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -132,6 +132,7 @@ static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn)
 #define DISAS_EXC 6
 /* WFE */
 #define DISAS_WFE 7
+#define DISAS_SEV 13
 #define DISAS_HVC 8
 #define DISAS_SMC 9
 #define DISAS_YIELD 10

ls1046ardb 仮想ボード側も GIC を接続するバスが間違っていたので、そこを修正すると secondary core も wfe を突破することはできるようになった。

しかし、 wfe 後に EL3 → EL2 と動作環境を切り替える処理があり、そこでハングしてしまった。

現在 ls1046ardb 仮想ボードは EL1 で動作している (EL2, EL3 を有効にすると起動そうそうにハングしていたので、無効化している) ので、以下のように u-boot 側を変更して、 EL の切り替えを発生させないようにすると、 kernel コードへ移動することができるようになった。

diff --git a/arch/arm/cpu/armv8/fsl-layerscape/lowlevel.S b/arch/arm/cpu/armv8/fsl-layerscape/lowlevel.S
index 28a31b21a9..2558481c80 100644
--- a/arch/arm/cpu/armv8/fsl-layerscape/lowlevel.S
+++ b/arch/arm/cpu/armv8/fsl-layerscape/lowlevel.S
@@ -73,7 +73,7 @@ ENDPROC(smp_kick_all_cpus)
 ENTRY(lowlevel_init)
        mov     x29, lr                 /* Save LR */
 
-       switch_el x1, 1f, 100f, 100f    /* skip if not in EL3 */
+       switch_el x1, 1f, 100f, 101f    /* skip if not in EL3 */
 1:
 
 #if defined (CONFIG_SYS_FSL_HAS_CCN504)
@@ -181,6 +181,7 @@ ENTRY(lowlevel_init)
        str     w0, [x1, #0x10]
 #endif
 
+101:
        /* Initialize GIC Secure Bank Status */
 #if defined(CONFIG_GICV2) || defined(CONFIG_GICV3)
        branch_if_slave x0, 1f
@@ -487,7 +488,8 @@ slave_cpu:
        ldr     x0, [x11]
        cbz     x0, slave_cpu
 #ifndef CONFIG_ARMV8_SWITCH_TO_EL1
-       mrs     x1, sctlr_el2
+       //mrs     x1, sctlr_el2
+       mrs     x1, sctlr_el1
 #else
        mrs     x1, sctlr_el1
 #endif
@@ -521,7 +523,7 @@ ENDPROC(secondary_boot_func)
 
 ENTRY(secondary_switch_to_el2)
        switch_el x6, 1f, 0f, 0f
-0:     ret
+0:     br x4
 1:     armv8_switch_to_el2_m x4, x5, x6
 ENDPROC(secondary_switch_to_el2)
 

kernel に入ったあとは、 CPU Idle Governor の初期化でハングが発生 (全コアが idle に入ってしまう?) してしまったのでデバッグ中。

GDB で追いかけているけど、問題の箇所付近で全然違う処理に飛んでしまい、 GDB が有効に活用できなくなって苦戦中。。


追記 (2017-12-17 13:56)

CPU Idle Governor を無効にしてみたけど、やっぱりハングするので、原因は違うところっぽい。。どうやって調査しようか。。