@@ -28,6 +28,7 @@ typedef enum {
2828 SYZOS_API_MEMWRITE ,
2929 SYZOS_API_ITS_SETUP ,
3030 SYZOS_API_ITS_SEND_CMD ,
31+ SYZOS_API_MRS ,
3132 SYZOS_API_STOP , // Must be the last one
3233} syzos_api_id ;
3334
@@ -41,6 +42,11 @@ struct api_call_uexit {
4142 uint64 exit_code ;
4243};
4344
45+ struct api_call_1 {
46+ struct api_call_header header ;
47+ uint64 arg ;
48+ };
49+
4450struct api_call_2 {
4551 struct api_call_header header ;
4652 uint64 args [2 ];
@@ -89,6 +95,7 @@ struct api_call_its_send_cmd {
8995
9096static void guest_uexit (uint64 exit_code );
9197static void guest_execute_code (uint32 * insns , uint64 size );
98+ static void guest_handle_mrs (uint64 reg );
9299static void guest_handle_msr (uint64 reg , uint64 val );
93100static void guest_handle_smc (struct api_call_smccc * cmd );
94101static void guest_handle_hvc (struct api_call_smccc * cmd );
@@ -128,6 +135,11 @@ guest_main(uint64 size, uint64 cpu)
128135 guest_execute_code (ccmd -> insns , cmd -> size - sizeof (struct api_call_header ));
129136 break ;
130137 }
138+ case SYZOS_API_MRS : {
139+ struct api_call_1 * ccmd = (struct api_call_1 * )cmd ;
140+ guest_handle_mrs (ccmd -> arg );
141+ break ;
142+ }
131143 case SYZOS_API_MSR : {
132144 struct api_call_2 * ccmd = (struct api_call_2 * )cmd ;
133145 guest_handle_msr (ccmd -> args [0 ], ccmd -> args [1 ]);
@@ -180,6 +192,7 @@ GUEST_CODE static noinline void guest_uexit(uint64 exit_code)
180192}
181193
182194#define MSR_REG_OPCODE 0xd5100000
195+ #define MRS_REG_OPCODE 0xd5300000
183196
184197// Generate an `MSR register, x0` instruction based on the register ID.
185198// Luckily for us, the five operands, Op0, Op1, CRn, CRm, and Op2 are laid out sequentially in
@@ -191,6 +204,12 @@ GUEST_CODE static uint32 reg_to_msr(uint64 reg)
191204 return MSR_REG_OPCODE | ((reg & 0xffff ) << 5 );
192205}
193206
207+ // Generate an `MRS register, x0` instruction based on the register ID.
208+ GUEST_CODE static uint32 reg_to_mrs (uint64 reg )
209+ {
210+ return MRS_REG_OPCODE | ((reg & 0xffff ) << 5 );
211+ }
212+
194213// Host sets TPIDR_EL1 to contain the virtual CPU id.
195214GUEST_CODE static uint32 get_cpu_id ()
196215{
@@ -203,6 +222,23 @@ GUEST_CODE static uint32 get_cpu_id()
203222// Some ARM chips use 128-byte cache lines. Pick 256 to be on the safe side.
204223#define MAX_CACHE_LINE_SIZE 256
205224
225+ // Read the value from a system register using an MRS instruction.
226+ GUEST_CODE static noinline void
227+ guest_handle_mrs (uint64 reg )
228+ {
229+ uint32 mrs = reg_to_mrs (reg );
230+ uint32 cpu_id = get_cpu_id ();
231+ // Make sure CPUs use different cache lines for scratch code.
232+ uint32 * insn = (uint32 * )((uint64 )ARM64_ADDR_SCRATCH_CODE + cpu_id * MAX_CACHE_LINE_SIZE );
233+ insn [0 ] = mrs ;
234+ insn [1 ] = 0xd65f03c0 ; // RET
235+ // Make a call to the generated MSR instruction and clobber x0.
236+ asm("blr %[pc]\n"
237+ :
238+ : [pc ] "r" (insn )
239+ : "x0" , "x30" );
240+ }
241+
206242// Write value to a system register using an MSR instruction.
207243// The word "MSR" here has nothing to do with the x86 MSR registers.
208244GUEST_CODE static noinline void
0 commit comments