Unable to Read/Write Non-Secure EL1 System Registers (e.g., SCTLR_EL1) from EL3 on FVP with OP-TEE and TF-A

Hi Everybody , 

I am working on the FVP_Base_RevC-2xAEMvA platform with the following setup:

  • Secure OS: OP-TEE

  • EL3 Monitor: Trusted Firmware-A (TF-A)

  • Non-Secure OS: Linux (running at EL1, no EL2/hypervisor)

I am trying to read and write EL1 system registers (e.g., SCTLR_EL1) of the non-secure world (Linux) from EL3 (secure monitor context) in the absence of EL2.

I am issuing an SMC from EL1 (non-secure) to EL3, and at EL3 I intend to read and modify system registers belonging to the Linux environment. The goal is for these changes made at EL3 to be reflected when execution returns to the non-secure world. I tried to toggle wxor bit of SCTLR_EL1 register so it is a valid change I also verified it by doing MSR instruction from Linux and I can see the change if I toggle that bit from Linux.

What I Did:

1. System Call in Linux (Non-Secure World)

  • I wrote a custom Linux system call that issues an SMC to EL3.

  • In this syscall, I read the value of SCTLR_EL1 before and after the SMC call, to check whether any changes made in EL3 were reflected.

2. I tried to write to system registers of Linux (Non secure wold EL1) at EL3 by two approaches.

A. Context Management API & Direct Register Access (in opteed_main.c)

  • I implemented a secure service handler inside opteed_main.c at EL3.

  • First, I attempted to read the value of SCTLR_EL1 using a direct MRS instruction inside this handler.
    However, the value retrieved was different from what I observed in Linux.

  • I then tried to update the value using MSR, but the modification did not reflect when control returned to the non-secure world.

  • Additionally, I used the TF-A context management APIs (e.g., write_ctx_reg, read_ctx_reg) to manipulate the non-secure context.
    Despite this, the values read were still different, and any modifications did not propagate back to the non-secure Linux environment.

B. Direct MSR from el3_exit() (Before ERET)

  • In the el3_exit() routine which is in runtime_exception.S file I wrote MSR instruction just before the eret instruction. I used MSR instruction here to change the value of this system register but  No visible change in SCTLR_EL1 in the non-secure world after returning from the SMC.
  • To verify that EL3 modifications propagate correctly, I tested writing to a general-purpose register from el3_exit() routine I used mv instruction to change the content of x28 before eret instruction,
  • This change was visible in the non-secure world. So, general-purpose register save/restore works as expected during SMC world switch.

I also came across a Trusted Firmware-A technical presentation discussing the context management library, where it was shown (via a diagram) that only general-purpose registers are saved when transitioning from the non-secure world to EL3. Here I attached that diagram and presentation for reference.

Presentation : www.trustedfirmware.org/.../RefactorContextMgmt.pdf

Based on this I have some doubts

Questions

  1. What happens to non-secure world EL1 system registers (e.g., SCTLR_EL1) during an SMC exception?

  2. Are these registers saved and restored by TF-A as part of the non-secure context management, or Hardware does that?

  3. Why am I getting different values from non secure Linux and from EL3 while reading the same register (I checked NS bit also I am reading the values of Non secure world copies only)?

  4. What is the correct or recommended way to access or modify non-secure EL1 system registers from EL3, especially when EL2 is not present?

Thank you in advance for any insights or guidance.

Best regards,
Dev