diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp index fbdd8d7a..3f6cca4d 100644 --- a/pvmfw/Android.bp +++ b/pvmfw/Android.bp @@ -28,6 +28,7 @@ cc_binary { name: "pvmfw", srcs: [ "entry.S", + "exceptions.S", "idmap.S", ], static_libs: [ diff --git a/pvmfw/entry.S b/pvmfw/entry.S index e5c6045a..f0021be4 100644 --- a/pvmfw/entry.S +++ b/pvmfw/entry.S @@ -145,6 +145,10 @@ entry: adr_l x30, boot_stack_end mov sp, x30 + /* Set up exception vector. */ + adr x30, vector_table_el1 + msr vbar_el1, x30 + /* Call into Rust code. */ bl main diff --git a/pvmfw/exceptions.S b/pvmfw/exceptions.S new file mode 100644 index 00000000..86ef83cf --- /dev/null +++ b/pvmfw/exceptions.S @@ -0,0 +1,178 @@ +/* + * 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 + * + * https://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. + */ + +/** + * Saves the volatile registers onto the stack. This currently takes 14 + * instructions, so it can be used in exception handlers with 18 instructions + * left. + * + * On return, x0 and x1 are initialised to elr_el2 and spsr_el2 respectively, + * which can be used as the first and second arguments of a subsequent call. + */ +.macro save_volatile_to_stack + /* Reserve stack space and save registers x0-x18, x29 & x30. */ + stp x0, x1, [sp, #-(8 * 24)]! + stp x2, x3, [sp, #8 * 2] + stp x4, x5, [sp, #8 * 4] + stp x6, x7, [sp, #8 * 6] + stp x8, x9, [sp, #8 * 8] + stp x10, x11, [sp, #8 * 10] + stp x12, x13, [sp, #8 * 12] + stp x14, x15, [sp, #8 * 14] + stp x16, x17, [sp, #8 * 16] + str x18, [sp, #8 * 18] + stp x29, x30, [sp, #8 * 20] + + /* + * Save elr_el1 & spsr_el1. This such that we can take nested exception + * and still be able to unwind. + */ + mrs x0, elr_el1 + mrs x1, spsr_el1 + stp x0, x1, [sp, #8 * 22] +.endm + +/** + * Restores the volatile registers from the stack. This currently takes 14 + * instructions, so it can be used in exception handlers while still leaving 18 + * instructions left; if paired with save_volatile_to_stack, there are 4 + * instructions to spare. + */ +.macro restore_volatile_from_stack + /* Restore registers x2-x18, x29 & x30. */ + ldp x2, x3, [sp, #8 * 2] + ldp x4, x5, [sp, #8 * 4] + ldp x6, x7, [sp, #8 * 6] + ldp x8, x9, [sp, #8 * 8] + ldp x10, x11, [sp, #8 * 10] + ldp x12, x13, [sp, #8 * 12] + ldp x14, x15, [sp, #8 * 14] + ldp x16, x17, [sp, #8 * 16] + ldr x18, [sp, #8 * 18] + ldp x29, x30, [sp, #8 * 20] + + /* Restore registers elr_el1 & spsr_el1, using x0 & x1 as scratch. */ + ldp x0, x1, [sp, #8 * 22] + msr elr_el1, x0 + msr spsr_el1, x1 + + /* Restore x0 & x1, and release stack space. */ + ldp x0, x1, [sp], #8 * 24 +.endm + +/** + * This is a generic handler for exceptions taken at the current EL while using + * SP0. It behaves similarly to the SPx case by first switching to SPx, doing + * the work, then switching back to SP0 before returning. + * + * Switching to SPx and calling the Rust handler takes 16 instructions. To + * restore and return we need an additional 16 instructions, so we can implement + * the whole handler within the allotted 32 instructions. + */ +.macro current_exception_sp0 handler:req + msr spsel, #1 + save_volatile_to_stack + bl \handler + restore_volatile_from_stack + msr spsel, #0 + eret +.endm + +/** + * This is a generic handler for exceptions taken at the current EL while using + * SPx. It saves volatile registers, calls the Rust handler, restores volatile + * registers, then returns. + * + * This also works for exceptions taken from EL0, if we don't care about + * non-volatile registers. + * + * Saving state and jumping to the Rust handler takes 15 instructions, and + * restoring and returning also takes 15 instructions, so we can fit the whole + * handler in 30 instructions, under the limit of 32. + */ +.macro current_exception_spx handler:req + save_volatile_to_stack + bl \handler + restore_volatile_from_stack + eret +.endm + +.section .text.vector_table_el1, "ax" +.global vector_table_el1 +.balign 0x800 +vector_table_el1: +sync_cur_sp0: + current_exception_sp0 sync_exception_current + +.balign 0x80 +irq_cur_sp0: + current_exception_sp0 irq_current + +.balign 0x80 +fiq_cur_sp0: + current_exception_sp0 fiq_current + +.balign 0x80 +serr_cur_sp0: + current_exception_sp0 serr_current + +.balign 0x80 +sync_cur_spx: + current_exception_spx sync_exception_current + +.balign 0x80 +irq_cur_spx: + current_exception_spx irq_current + +.balign 0x80 +fiq_cur_spx: + current_exception_spx fiq_current + +.balign 0x80 +serr_cur_spx: + current_exception_spx serr_current + +.balign 0x80 +sync_lower_64: + current_exception_spx sync_lower + +.balign 0x80 +irq_lower_64: + current_exception_spx irq_lower + +.balign 0x80 +fiq_lower_64: + current_exception_spx fiq_lower + +.balign 0x80 +serr_lower_64: + current_exception_spx serr_lower + +.balign 0x80 +sync_lower_32: + current_exception_spx sync_lower + +.balign 0x80 +irq_lower_32: + current_exception_spx irq_lower + +.balign 0x80 +fiq_lower_32: + current_exception_spx fiq_lower + +.balign 0x80 +serr_lower_32: + current_exception_spx serr_lower diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs new file mode 100644 index 00000000..2bdcf9ce --- /dev/null +++ b/pvmfw/src/exceptions.rs @@ -0,0 +1,81 @@ +// 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. + +//! Exception handlers. + +use crate::console::emergency_write_str; +use crate::eprintln; +use crate::psci::system_reset; +use core::arch::asm; + +#[no_mangle] +extern "C" fn sync_exception_current() { + emergency_write_str("sync_exception_current\n"); + print_esr(); + system_reset(); +} + +#[no_mangle] +extern "C" fn irq_current() { + emergency_write_str("irq_current\n"); + system_reset(); +} + +#[no_mangle] +extern "C" fn fiq_current() { + emergency_write_str("fiq_current\n"); + system_reset(); +} + +#[no_mangle] +extern "C" fn serr_current() { + emergency_write_str("serr_current\n"); + print_esr(); + system_reset(); +} + +#[no_mangle] +extern "C" fn sync_lower() { + emergency_write_str("sync_lower\n"); + print_esr(); + system_reset(); +} + +#[no_mangle] +extern "C" fn irq_lower() { + emergency_write_str("irq_lower\n"); + system_reset(); +} + +#[no_mangle] +extern "C" fn fiq_lower() { + emergency_write_str("fiq_lower\n"); + system_reset(); +} + +#[no_mangle] +extern "C" fn serr_lower() { + emergency_write_str("serr_lower\n"); + print_esr(); + system_reset(); +} + +#[inline] +fn print_esr() { + let mut esr: u64; + unsafe { + asm!("mrs {esr}, esr_el1", esr = out(reg) esr); + } + eprintln!("esr={:#08x}", esr); +} diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs index 4ab14b7f..d38b1e3d 100644 --- a/pvmfw/src/main.rs +++ b/pvmfw/src/main.rs @@ -18,6 +18,7 @@ #![no_std] mod console; +mod exceptions; mod psci; mod uart;