Cloud Hypervisor + GDB + Arm64 Part 6: Accessing System Registers
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 (forKVM_SET_ONE_REG
) or the value received (forKVM_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>;