Cloud Hypervisor + GDB + Arm64 Part 6: Accessing System Registers

Michael Zhao
3 min readJul 11, 2022

--

During address translation on AArch64, we need to access some system registers like TCR_ELx, TTBR_ELx and etc. The access is done via system register instructions.

In this article, I will introduce how the instructions are constructed and how it should work in Rust with KVM.

System Registers on AArch64

System Register Instructions

AArch64 provides 2 instructions to access system registers:

The instructions format:

MSR <System register>, Xt     ; Write to System register
MRS Xt, <System register> ; Read from System register

System Instruction Class Encoding

The encoding of the System instruction class describes each instruction as being either:

  • A transfer to a System register. This is a System instruction with the semantics of a write.
  • A transfer from a System register. This is a System instruction with the semantics of a read.

An encoding in the System instruction space is identified by a set of arguments: {op0, op1, CRn, CRm, op2}. The structure of the system instruction class is:

L:

  • 0 Transfer to System register.
  • 1 Transfer from System register.

op0:

  • 0b00: architectural hints, barriers and CLREX, and PSTATE access
  • 0b01: cache maintenance, TLB maintenance, address translation, prediction restriction, and BRBE instructions
  • 0b10: legacy AArch32 system registers, debug and trace registers.
  • 0b11: moves to and from non-debug system registers and special-purpose registers

op1:

Identifies the lowest Exception level at which the encoding is accessible:

  • Accessible at EL0: op1 = 3
  • Accessible at EL1: op1 = 0, 1, or 2
  • Accessible at Secure EL1: op1 = 7
  • Accessible at EL2: op1 = 4 or 5
  • Accessible at EL3: op1 = 6

The registers used in address translation belong to non-debug system registers (op0 == 0b11). Next I only introduce how this class of registers are constructed.

Encoding of Non-debug System registers

The instructions that move data to and from non-debug System registers are encoded with op0==0b11, except that some of this encoding space is reserved for IMPLEMENTATION DEFINED functionality. The encoding of these
instructions is:

Table D12–2 includes the encodings of some non-debug system registers that we are interested for address translation: ID_AA64MMFR0_EL1, TTBR0_EL1, TTBR1_EL1 and TCR_EL1. The following snapshot is part of the table that contains these registers.

Accessing System Registers on KVM

For KVM on AArch64, we can use 2 APIs to read and write system registers:

  • KVM_GET_ONE_REG
  • KVM_SET_ONE_REG

Both APIs take a parameter in struct:

struct kvm_one_reg {
__u64 id;
__u64 addr;
};
  • id: The ID of a register. For our address translation scenario, this should be the encoded system register ID introduced above, 32-bit.
  • addr: The value to set (for KVM_SET_ONE_REG) or the value received (for KVM_GET_ONE_REG).

To invoke the APIs, kvm-ioctls crate provides wrappers in VcpuFd struct:

pub fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()>;pub fn get_one_reg(&self, reg_id: u64) -> Result<u64>;

Reference

--

--

Michael Zhao
Michael Zhao

Written by Michael Zhao

Major in virtualization, security and ARM.

No responses yet