66// option. This file may not be copied, modified, or distributed
77// except according to those terms.
88
9- use core:: arch:: naked_asm;
10-
9+ use crate :: {
10+ arch,
11+ platform:: { Platform , PlatformImpl } ,
12+ simple_map:: SimpleMap ,
13+ } ;
14+ use aarch64_paging:: descriptor:: Stage2Attributes ;
15+ use aarch64_paging:: idmap:: IdMap ;
1116use aarch64_rt:: { RegisterStateRef , Stack } ;
1217use arm_sysregs:: {
13- CnthctlEl2 , CntvoffEl2 , ElrEl2 , HcrEl2 , MpidrEl1 , SpsrEl2 , read_cnthctl_el2, read_esr_el2,
14- read_far_el2, read_hcr_el2, read_mpidr_el1, read_spsr_el2, write_cnthctl_el2,
15- write_cntvoff_el2, write_elr_el2, write_hcr_el2, write_spsr_el2,
18+ CnthctlEl2 , CntvoffEl2 , ElrEl1 , ElrEl2 , EsrEl1 , FarEl1 , HcrEl2 , MpidrEl1 , SpsrEl1 , SpsrEl2 ,
19+ VtcrEl2 , read_cnthctl_el2, read_esr_el2, read_far_el2, read_hcr_el2, read_mpidr_el1,
20+ read_spsr_el2, read_vbar_el1, write_cnthctl_el2, write_cntvoff_el2, write_elr_el1,
21+ write_elr_el2, write_esr_el1, write_far_el1, write_hcr_el2, write_spsr_el1, write_spsr_el2,
22+ write_vtcr_el2,
1623} ;
24+ use core:: arch:: naked_asm;
1725use log:: debug;
26+ use spin:: Once ;
1827use spin:: mutex:: SpinMutex ;
1928
20- use crate :: {
21- platform:: { Platform , PlatformImpl } ,
22- simple_map:: SimpleMap ,
23- } ;
29+ static STAGE2_MAP : Once < SpinMutex < IdMap < Stage2Attributes > > > = Once :: new ( ) ;
2430
2531const SPSR_EL1H : u8 = 5 ;
32+ const T0SZ_MAX_SIZE : u8 = 64 ;
2633
2734/// Entry point for EL1 execution.
2835///
@@ -35,10 +42,12 @@ const SPSR_EL1H: u8 = 5;
3542/// address for EL1 execution that never returns.
3643/// This function must be called in EL2.
3744pub unsafe fn entry_point_el1 ( arg0 : u64 , arg1 : u64 , arg2 : u64 , arg3 : u64 , entry_point : u64 ) -> ! {
45+ setup_stage2 ( ) ;
3846 // Setup EL1
3947 let mut hcr = read_hcr_el2 ( ) ;
4048 hcr |= HcrEl2 :: RW ;
4149 hcr |= HcrEl2 :: TSC ;
50+ hcr |= HcrEl2 :: VM ;
4251 hcr -= HcrEl2 :: IMO ;
4352 // SAFETY: We are configuring HCR_EL2 to allow EL1 execution.
4453 unsafe {
@@ -83,6 +92,39 @@ pub unsafe fn entry_point_el1(arg0: u64, arg1: u64, arg2: u64, arg3: u64, entry_
8392 }
8493}
8594
95+ fn setup_stage2 ( ) {
96+ debug ! ( "Setting up stage 2 page table" ) ;
97+ let mut idmap = STAGE2_MAP
98+ . call_once ( || SpinMutex :: new ( PlatformImpl :: make_stage2_pagetable ( ) ) )
99+ . lock ( ) ;
100+
101+ let root_pa = idmap. root_address ( ) . 0 ;
102+ debug ! ( "Root PA: {root_pa:#x}" ) ;
103+
104+ // Activate the page table
105+ // SAFETY: We are initializing the Stage 2 translation. The guest is not running yet.
106+ let ttbr = unsafe { idmap. activate ( ) } ;
107+ debug ! ( "idmap.activate() returned ttbr={ttbr:#x}" ) ;
108+
109+ let mut vtcr = VtcrEl2 :: default ( ) ;
110+ vtcr. set_ps ( 2 ) ; // 40 bit physical address size
111+ vtcr. set_tg0 ( 0 ) ; // 4kB granule size
112+ vtcr. set_sh0 ( 3 ) ; // Inner shareable memory
113+ vtcr. set_orgn0 ( 1 ) ; // Outer Write-Back Read-Allocate Write-Allocate Cacheable
114+ vtcr. set_irgn0 ( 1 ) ; // Inner Write-Back Read-Allocate Write-Allocate Cacheable
115+ vtcr. set_sl0 ( 2 ) ; // L0 starting level
116+ vtcr. set_t0sz ( T0SZ_MAX_SIZE - 40 ) ; // 40 bit size offset
117+ debug ! ( "Writing VTCR_EL2={vtcr:#x}..." ) ;
118+ // SAFETY: We are initializing the Stage 2 translation. The guest is not running yet.
119+ unsafe {
120+ write_vtcr_el2 ( vtcr) ;
121+ }
122+ arch:: tlbi_vmalls12e1 ( ) ;
123+ arch:: dsb ( ) ;
124+ arch:: isb ( ) ;
125+ debug ! ( "Stage 2 activation complete." ) ;
126+ }
127+
86128/// Returns to EL1.
87129///
88130/// This function executes the `eret` instruction to return to EL1 with the provided arguments.
@@ -179,15 +221,61 @@ pub fn handle_sync_lower(mut register_state: RegisterStateRef) {
179221 }
180222 }
181223 }
182- ExceptionClass :: Unknown ( _) => {
224+ ExceptionClass :: DataAbortLowerEL => {
225+ inject_data_abort ( & mut register_state) ;
226+ }
227+ ExceptionClass :: Unknown ( val) => {
183228 panic ! (
184- "Unexpected sync_lower, esr={esr_el2:#x}, far={:#x}, register_state={register_state:?}" ,
229+ "Unexpected sync_lower, esr={esr_el2:#x}, ec={val:#x}, far={:#x}, register_state={register_state:?}" ,
185230 read_far_el2( ) ,
186231 ) ;
187232 }
188233 }
189234}
190235
236+ fn inject_data_abort ( register_state : & mut RegisterStateRef ) {
237+ // SAFETY: We are modifying the saved register state to redirect execution.
238+ let regs = unsafe { register_state. get_mut ( ) } ;
239+ let fault_addr = read_far_el2 ( ) ;
240+ let esr = read_esr_el2 ( ) ;
241+
242+ debug ! ( "Injecting data abort to guest: fault_addr={fault_addr:#x}, esr={esr:#x}" ) ;
243+
244+ // Read guest VBAR
245+ let vbar = read_vbar_el1 ( ) . bits ( ) ;
246+ assert_ne ! (
247+ vbar, 0 ,
248+ "Guest VBAR_EL1 is 0, cannot inject data abort. Fault addr: {fault_addr:#x}"
249+ ) ;
250+ let handler = vbar + 0x200 ; // Current EL with SPx Sync
251+
252+ // Save current context to guest EL1 regs
253+ // SAFETY: We are accessing EL1 system registers to inject exception.
254+ unsafe {
255+ write_elr_el1 ( ElrEl1 :: from_bits_retain ( regs. elr as u64 ) ) ;
256+ write_spsr_el1 ( SpsrEl1 :: from_bits_retain ( regs. spsr ) ) ;
257+ write_far_el1 ( FarEl1 :: from_bits_retain ( fault_addr. va ( ) ) ) ;
258+ }
259+ write_esr_el1 ( EsrEl1 :: from_bits_retain ( esr. bits ( ) ) ) ;
260+
261+ // Redirect execution
262+ #[ expect(
263+ clippy:: cast_possible_truncation,
264+ reason = "only 64-bit target is supported"
265+ ) ]
266+ {
267+ regs. elr = handler as usize ;
268+ }
269+ // Mask all interrupts (DAIF) and set mode to EL1h
270+ let mut spsr = SpsrEl1 :: default ( ) ;
271+ spsr. set_m_3_0 ( SPSR_EL1H ) ;
272+ spsr |= SpsrEl1 :: D ;
273+ spsr |= SpsrEl1 :: A ;
274+ spsr |= SpsrEl1 :: I ;
275+ spsr |= SpsrEl1 :: F ;
276+ regs. spsr = spsr. bits ( ) ;
277+ }
278+
191279const AARCH64_INSTRUCTION_LENGTH : usize = 4 ;
192280
193281fn try_handle_psci ( register_state : & mut RegisterStateRef ) -> Result < ( ) , arm_psci:: Error > {
@@ -369,6 +457,8 @@ enum ExceptionClass {
369457 HvcTrappedInAArch64 ,
370458 /// SMC instruction execution in `AArch64` state.
371459 SmcTrappedInAArch64 ,
460+ /// Data Abort taken without a change in Exception Level.
461+ DataAbortLowerEL ,
372462 #[ allow( unused) ]
373463 /// Unknown exception class.
374464 Unknown ( u8 ) ,
@@ -379,6 +469,7 @@ impl From<u8> for ExceptionClass {
379469 match value {
380470 0x16 => Self :: HvcTrappedInAArch64 ,
381471 0x17 => Self :: SmcTrappedInAArch64 ,
472+ 0x24 => Self :: DataAbortLowerEL ,
382473 _ => Self :: Unknown ( value) ,
383474 }
384475 }
0 commit comments