pvmfw: Issue MMIO_GUARD_MAP for UART
Share the MMIO page containing the UART with the VMM.
When handling a synchronous exception, don't print to the UART if it
would cause re-entering the handler.
Bug: 237371962
Test: m pvmfw_img && flash pvmfw.img && access UART
Change-Id: Ia384f2e3a15b40b5e67cfafd50e571848fd5a126
diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs
index 596ecc7..0fb2911 100644
--- a/pvmfw/src/exceptions.rs
+++ b/pvmfw/src/exceptions.rs
@@ -14,14 +14,23 @@
//! Exception handlers.
+use crate::helpers::page_4kb_of;
use core::arch::asm;
+use vmbase::console;
use vmbase::{console::emergency_write_str, eprintln, power::reboot};
+const ESR_32BIT_EXT_DABT: u64 = 0x96000010;
+const UART_PAGE: u64 = page_4kb_of(console::BASE_ADDRESS as u64);
+
#[no_mangle]
extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
let esr = read_esr();
- emergency_write_str("sync_exception_current\n");
- print_esr(esr);
+ let far = read_far();
+ // Don't print to the UART if we're handling the exception it could raise.
+ if esr != ESR_32BIT_EXT_DABT || page_4kb_of(far) != UART_PAGE {
+ emergency_write_str("sync_exception_current\n");
+ print_esr(esr);
+ }
reboot();
}
@@ -86,3 +95,12 @@
fn print_esr(esr: u64) {
eprintln!("esr={:#08x}", esr);
}
+
+#[inline]
+fn read_far() -> u64 {
+ let mut far: u64;
+ unsafe {
+ asm!("mrs {far}, far_el1", far = out(reg) far);
+ }
+ far
+}
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
new file mode 100644
index 0000000..781c1ac
--- /dev/null
+++ b/pvmfw/src/helpers.rs
@@ -0,0 +1,36 @@
+// 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.
+
+//! Miscellaneous helper functions.
+
+/// Computes the address of the page containing a given address.
+pub const fn page_of(addr: u64, page_size: u64) -> u64 {
+ addr & !(page_size - 1)
+}
+
+/// Validates a page size and computes the address of the page containing a given address.
+pub const fn checked_page_of(addr: u64, page_size: u64) -> Option<u64> {
+ if page_size.is_power_of_two() {
+ Some(page_of(addr, page_size))
+ } else {
+ None
+ }
+}
+
+/// Computes the address of the 4KiB page containing a given address.
+pub const fn page_4kb_of(addr: u64) -> u64 {
+ const PAGE_SIZE: u64 = 4 << 10;
+
+ page_of(addr, PAGE_SIZE)
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 5f918fb..eb97961 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -18,25 +18,36 @@
#![no_std]
mod exceptions;
+mod helpers;
+mod smccc;
use core::fmt;
+use helpers::checked_page_of;
-use vmbase::{main, power::reboot, println};
+use vmbase::{console, main, power::reboot, println};
#[derive(Debug, Clone)]
-enum Error {}
+enum Error {
+ /// Failed to configure the UART; no logs available.
+ FailedUartSetup,
+}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- #[allow(clippy::match_single_binding)]
let msg = match self {
- _ => "",
+ Self::FailedUartSetup => "Failed to configure the UART",
};
write!(f, "{}", msg)
}
}
fn main(fdt_address: u64, payload_start: u64, payload_size: u64, arg3: u64) -> Result<(), Error> {
+ // We need to inform the hypervisor that the MMIO page containing the UART may be shared back.
+ let uart = console::BASE_ADDRESS as u64;
+ let mmio_granule = smccc::mmio_guard_info().map_err(|_| Error::FailedUartSetup)?;
+ let uart_page = checked_page_of(uart, mmio_granule).ok_or(Error::FailedUartSetup)?;
+ smccc::mmio_guard_map(uart_page).map_err(|_| Error::FailedUartSetup)?;
+
println!("pVM firmware");
println!(
"fdt_address={:#018x}, payload_start={:#018x}, payload_size={:#018x}, x3={:#018x}",
diff --git a/pvmfw/src/smccc.rs b/pvmfw/src/smccc.rs
new file mode 100644
index 0000000..e3a2b05
--- /dev/null
+++ b/pvmfw/src/smccc.rs
@@ -0,0 +1,115 @@
+// 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.
+
+use core::fmt;
+
+// TODO(b/245889995): use psci-0.1.1 crate
+#[inline(always)]
+fn hvc64(function: u32, args: [u64; 17]) -> [u64; 18] {
+ #[cfg(target_arch = "aarch64")]
+ unsafe {
+ let mut ret = [0; 18];
+
+ core::arch::asm!(
+ "hvc #0",
+ inout("x0") function as u64 => ret[0],
+ inout("x1") args[0] => ret[1],
+ inout("x2") args[1] => ret[2],
+ inout("x3") args[2] => ret[3],
+ inout("x4") args[3] => ret[4],
+ inout("x5") args[4] => ret[5],
+ inout("x6") args[5] => ret[6],
+ inout("x7") args[6] => ret[7],
+ inout("x8") args[7] => ret[8],
+ inout("x9") args[8] => ret[9],
+ inout("x10") args[9] => ret[10],
+ inout("x11") args[10] => ret[11],
+ inout("x12") args[11] => ret[12],
+ inout("x13") args[12] => ret[13],
+ inout("x14") args[13] => ret[14],
+ inout("x15") args[14] => ret[15],
+ inout("x16") args[15] => ret[16],
+ inout("x17") args[16] => ret[17],
+ options(nomem, nostack)
+ );
+
+ ret
+ }
+}
+
+/// Standard SMCCC error values as described in DEN 0028E.
+#[derive(Debug, Clone)]
+pub enum Error {
+ /// The call is not supported by the implementation.
+ NotSupported,
+ /// The call is deemed not required by the implementation.
+ NotRequired,
+ /// One of the call parameters has a non-supported value.
+ InvalidParameter,
+ /// Negative values indicate error.
+ Unknown(i64),
+ /// The call returned a positive value when 0 was expected.
+ Unexpected(u64),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::NotSupported => write!(f, "SMCCC call not supported"),
+ Self::NotRequired => write!(f, "SMCCC call not required"),
+ Self::InvalidParameter => write!(f, "SMCCC call received non-supported value"),
+ Self::Unexpected(v) => write!(f, "Unexpected SMCCC return value '{}'", v),
+ Self::Unknown(e) => write!(f, "Unknown SMCCC return value '{}'", e),
+ }
+ }
+}
+
+fn check_smccc_err(ret: i64) -> Result<(), Error> {
+ match check_smccc_value(ret)? {
+ 0 => Ok(()),
+ v => Err(Error::Unexpected(v)),
+ }
+}
+
+fn check_smccc_value(ret: i64) -> Result<u64, Error> {
+ match ret {
+ x if x >= 0 => Ok(ret as u64),
+ -1 => Err(Error::NotSupported),
+ -2 => Err(Error::NotRequired),
+ -3 => Err(Error::InvalidParameter),
+ _ => Err(Error::Unknown(ret)),
+ }
+}
+
+const VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID: u32 = 0xc6000005;
+const VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
+
+/// Issue pKVM-specific MMIO_GUARD_INFO HVC64.
+pub fn mmio_guard_info() -> Result<u64, Error> {
+ let args = [0u64; 17];
+
+ let res = hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args);
+
+ check_smccc_value(res[0] as i64)
+}
+
+/// Issue pKVM-specific MMIO_GUARD_MAP HVC64.
+pub fn mmio_guard_map(ipa: u64) -> Result<(), Error> {
+ let mut args = [0u64; 17];
+ args[0] = ipa;
+
+ let res = hvc64(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args);
+
+ check_smccc_err(res[0] as i64)
+}