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 を無効にしてみたけど、やっぱりハングするので、原因は違うところっぽい。。どうやって調査しようか。。