Peter Maydell <peter.mayd...@linaro.org> writes: > On Sat, 14 Jun 2025 at 13:50, <r...@wjsota.com> wrote: >> >> Hi! >> >> Is `qemu-aarch64 -cpu neoverse-n1` supposed to emulate the `retaa` >> instruction? >> >> I have a binary called `main_pac` compiled from >> https://learn.arm.com/learning-paths/servers-and-cloud-computing/pac/example/ >> . >> >> The compiling command is `aarch64-linux-gnu-gcc -march=armv8.5-a >> -fPIC -pedantic -Wall -Wextra -ggdb3 -O0 >> -mbranch-protection=standard -fno-stack-protector -fPIE -static >> main.c -o main_pac`. The binary includes the `paciasp` and `retaa` >> instructions associated with ARM PAC. >> >> ``` >> (gdb) disas main >> Dump of assembler code for function main: >> 0x0000000000400858 <+0>: paciasp
This instruction is in the HINT opcode space so can be NOP. >> 0x000000000040085c <+4>: stp x29, x30, [sp, #-32]! >> […] >> 0x0000000000400898 <+64>: ldp x29, x30, [sp], #32 >> 0x000000000040089c <+68>: retaa >> End of assembler dump. >> (gdb) quit >> ``` >> >> When emulated using `qemu-aarch64 -cpu neoverse-n1` , the program completes >> without issues. >> ``` >> user@dell-op7020:~/learning/arm_learning_path_pac$ qemu-aarch64 -cpu >> neoverse-n1 main_pac test >> Hello World! The code just does: static bool trans_RETA(DisasContext *s, arg_reta *a) { TCGv_i64 dst; dst = auth_branch_target(s, cpu_reg(s, 30), cpu_X[31], !a->m); gen_a64_set_pc(s, dst); s->base.is_jmp = DISAS_JUMP; return true; } Where auth_branch_target does: static TCGv_i64 auth_branch_target(DisasContext *s, TCGv_i64 dst, TCGv_i64 modifier, bool use_key_a) { TCGv_i64 truedst; /* * Return the branch target for a BRAA/RETA/etc, which is either * just the destination dst, or that value with the pauth check * done and the code removed from the high bits. */ if (!s->pauth_active) { return dst; } truedst = tcg_temp_new_i64(); if (use_key_a) { gen_helper_autia_combined(truedst, tcg_env, dst, modifier); } else { gen_helper_autib_combined(truedst, tcg_env, dst, modifier); } return truedst; } Essentially if no pauth is active then just returns dest. I think this matches the pseudocode: GCSInstruction inst_type; bits(64) target = X[30, 64]; constant bits(64) modifier = SP[64]; bits(64) modifier2; boolean use_modifier2 = FALSE; if IsFeatureImplemented(FEAT_PAuth_LR) && PSTATE.PACM == '1' then modifier2 = X[16, 64]; use_modifier2 = TRUE; if use_key_a then if use_modifier2 && IsFeatureImplemented(FEAT_PAuth_LR) then target = AuthIA2(target, modifier, modifier2, auth_then_branch); else target = AuthIA(target, modifier, auth_then_branch); else if use_modifier2 && IsFeatureImplemented(FEAT_PAuth_LR) then target = AuthIB2(target, modifier, modifier2, auth_then_branch); else target = AuthIB(target, modifier, auth_then_branch); if IsFeatureImplemented(FEAT_GCS) && GCSPCREnabled(PSTATE.EL) then inst_type = if use_key_a then GCSInstType_PRETAA else GCSInstType_PRETAB; target = LoadCheckGCSRecord(target, inst_type); SetCurrentGCSPointer(GetCurrentGCSPointer() + 8); // Value in BTypeNext will be used to set PSTATE.BTYPE BTypeNext = '00'; constant boolean branch_conditional = FALSE; BranchTo(target, BranchType_RET, branch_conditional); which has no explicit UNDEF for the instruction. I think the fall-through is to AuthIB: bits(64) AuthIB(bits(64) x, bits(64) y, boolean is_combined) constant bits(128) APIBKey_EL1 = APIBKeyHi_EL1<63:0> : APIBKeyLo_EL1<63:0>; if !IsAPIBKeyEnabled() then return x; else return Auth(x, y, APIBKey_EL1, FALSE, '1', is_combined); So I think the correct response if no key is enabled is to return the address as is unmolested. Which makes sense as you want PAuth code to be run-able unaltered on systems without it. > > This might be a bug, but why are you compiling for armv8.5 and > then trying to run the code on a CPU type that isn't armv8.5 > in the first place? > > thanks > -- PMM -- Alex Bennée Virtualisation Tech Lead @ Linaro