| // Copyright 2022, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! ARM64 low-level payload entry point |
| |
| use crate::memory::MemorySlices; |
| use core::arch::asm; |
| use core::mem::size_of; |
| use vmbase::util::RangeExt as _; |
| use vmbase::{arch::aarch64::min_dcache_line_size, layout, memory::deactivate_dynamic_page_tables}; |
| |
| /// Function boot payload after cleaning all secret from pvmfw memory |
| pub fn jump_to_payload(entrypoint: usize, slices: &MemorySlices) -> ! { |
| let fdt_address = slices.fdt.as_ptr() as usize; |
| let dice_handover = slices.dice_handover.map(|slice| { |
| let r = slice.as_ptr_range(); |
| (r.start as usize)..(r.end as usize) |
| }); |
| |
| deactivate_dynamic_page_tables(); |
| |
| const ASM_STP_ALIGN: usize = size_of::<u64>() * 2; |
| const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11); |
| // Stage 1 instruction access cacheability is unaffected. |
| const SCTLR_EL1_I: u64 = 0b1 << 12; |
| // SETEND instruction disabled at EL0 in aarch32 mode. |
| const SCTLR_EL1_SED: u64 = 0b1 << 8; |
| // Various IT instructions are disabled at EL0 in aarch32 mode. |
| const SCTLR_EL1_ITD: u64 = 0b1 << 7; |
| |
| const SCTLR_EL1_VAL: u64 = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I; |
| |
| let scratch = layout::data_bss_range(); |
| |
| assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty."); |
| assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned."); |
| assert_eq!(scratch.end.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned."); |
| |
| // A sub-region of the scratch memory might contain data for the next stage so skip zeroing it. |
| // Alternatively, an empty region at the start of the scratch region is compatible with the ASM |
| // implementation and results in the whole scratch region being zeroed. |
| let skipped = dice_handover.unwrap_or(scratch.start.0..scratch.start.0); |
| |
| assert!(skipped.is_within(&(scratch.start.0..scratch.end.0))); |
| assert_eq!(skipped.start % ASM_STP_ALIGN, 0, "Misaligned skipped region."); |
| assert_eq!(skipped.end % ASM_STP_ALIGN, 0, "Misaligned skipped region."); |
| |
| let stack = layout::stack_range(); |
| |
| assert_ne!(stack.end - stack.start, 0, "stack region is empty."); |
| assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region."); |
| assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region."); |
| |
| let eh_stack = layout::eh_stack_range(); |
| |
| assert_ne!(eh_stack.end - eh_stack.start, 0, "EH stack region is empty."); |
| assert_eq!(eh_stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region."); |
| assert_eq!(eh_stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned EH stack region."); |
| |
| // Zero all memory that could hold secrets and that can't be safely written to from Rust. |
| // Disable the exception vector, caches and page table and then jump to the payload at the |
| // given address, passing it the given FDT pointer. |
| // |
| // SAFETY: We're exiting pvmfw by passing the register values we need to a noreturn asm!(). |
| unsafe { |
| asm!( |
| // Zero .data & .bss until the start of the skipped region. |
| "b 1f", |
| "0: stp xzr, xzr, [{scratch}], 16", |
| "1: cmp {scratch}, {skipped}", |
| "b.lo 0b", |
| |
| // Skip the skipped region. |
| "mov {scratch}, {skipped_end}", |
| |
| // Keep zeroing .data & .bss. |
| "b 1f", |
| "0: stp xzr, xzr, [{scratch}], 16", |
| "1: cmp {scratch}, {scratch_end}", |
| "b.lo 0b", |
| |
| // Flush d-cache over .data & .bss (including skipped region). |
| "0: dc cvac, {cache_line}", |
| "add {cache_line}, {cache_line}, {dcache_line_size}", |
| "cmp {cache_line}, {scratch_end}", |
| "b.lo 0b", |
| |
| "mov {cache_line}, {stack}", |
| // Zero stack region. |
| "0: stp xzr, xzr, [{stack}], 16", |
| "cmp {stack}, {stack_end}", |
| "b.lo 0b", |
| |
| // Flush d-cache over stack region. |
| "0: dc cvac, {cache_line}", |
| "add {cache_line}, {cache_line}, {dcache_line_size}", |
| "cmp {cache_line}, {stack_end}", |
| "b.lo 0b", |
| |
| "mov {cache_line}, {eh_stack}", |
| // Zero EH stack region. |
| "0: stp xzr, xzr, [{eh_stack}], 16", |
| "cmp {eh_stack}, {eh_stack_end}", |
| "b.lo 0b", |
| |
| // Flush d-cache over EH stack region. |
| "0: dc cvac, {cache_line}", |
| "add {cache_line}, {cache_line}, {dcache_line_size}", |
| "cmp {cache_line}, {eh_stack_end}", |
| "b.lo 0b", |
| |
| "msr sctlr_el1, {sctlr_el1_val}", |
| "isb", |
| "mov x1, xzr", |
| "mov x2, xzr", |
| "mov x3, xzr", |
| "mov x4, xzr", |
| "mov x5, xzr", |
| "mov x6, xzr", |
| "mov x7, xzr", |
| "mov x8, xzr", |
| "mov x9, xzr", |
| "mov x10, xzr", |
| "mov x11, xzr", |
| "mov x12, xzr", |
| "mov x13, xzr", |
| "mov x14, xzr", |
| "mov x15, xzr", |
| "mov x16, xzr", |
| "mov x17, xzr", |
| "mov x18, xzr", |
| "mov x19, xzr", |
| "mov x20, xzr", |
| "mov x21, xzr", |
| "mov x22, xzr", |
| "mov x23, xzr", |
| "mov x24, xzr", |
| "mov x25, xzr", |
| "mov x26, xzr", |
| "mov x27, xzr", |
| "mov x28, xzr", |
| "mov x29, xzr", |
| "msr ttbr0_el1, xzr", |
| // Ensure that CMOs have completed before entering payload. |
| "dsb nsh", |
| "br x30", |
| sctlr_el1_val = in(reg) SCTLR_EL1_VAL, |
| skipped = in(reg) u64::try_from(skipped.start).unwrap(), |
| skipped_end = in(reg) u64::try_from(skipped.end).unwrap(), |
| cache_line = in(reg) u64::try_from(scratch.start.0).unwrap(), |
| scratch = in(reg) u64::try_from(scratch.start.0).unwrap(), |
| scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(), |
| stack = in(reg) u64::try_from(stack.start.0).unwrap(), |
| stack_end = in(reg) u64::try_from(stack.end.0).unwrap(), |
| eh_stack = in(reg) u64::try_from(eh_stack.start.0).unwrap(), |
| eh_stack_end = in(reg) u64::try_from(eh_stack.end.0).unwrap(), |
| dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(), |
| in("x0") u64::try_from(fdt_address).unwrap(), |
| in("x30") u64::try_from(entrypoint).unwrap(), |
| options(noreturn), |
| ); |
| }; |
| } |