1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use erased_serde::Serialize;
/// `SerializableError` is a trait which represents an error type that can
/// be sent over the wire by the proxy protocol.
#[typetag::serde(tag = "error_type")]
pub trait SerializableError: Serialize + std::error::Error + std::fmt::Debug + Send + Sync {
fn as_anyhow_error(self: Box<Self>) -> anyhow::Error;
}
/// `impl_serializable_error` needs to be invoked for every error type
/// that can be sent over the wire.
#[macro_export]
macro_rules! impl_serializable_error {
($t:ty) => {
const _: () = {
use $crate::proxy::errors::SerializableError;
#[typetag::serde]
impl SerializableError for $t {
fn as_anyhow_error(self: Box<$t>) -> anyhow::Error {
self.into()
}
}
};
};
}
/// `SerializedError` is the wire form of errors that can be sent through the proxy.
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct SerializedError {
pub description: String,
pub backtrace: String,
pub error: Option<Box<dyn SerializableError>>,
}
// Helper macro for building the `From` implementation that converts
// native error types into their serialized form. This is a recursive
// macro.
macro_rules! to_serialized_error {
// This is the terminal state for this macro: only one type to convert.
// Parameters:
// error - The error object.
// msg - The error message (e.g. `to_string()`).
// bt - The error backtrace.
// box - A function which can box the error if needed.
// t - The error type to convert.
($error:expr, $msg:expr, $bt:expr, $box:expr, $t:ty $(,)?) => {{
match $error.downcast::<$t>() {
Ok(e) => return SerializedError {
description: $msg,
backtrace: $bt,
error: Some($box(e)),
},
Err(_) => return SerializedError {
description: $msg,
backtrace: $bt,
error: None,
},
}
}};
// This is the non-terminal state for this macro: many types to convert.
// Parameters:
// error - The error object.
// msg - The error message (e.g. `to_string()`).
// bt - The error backtrace.
// box - A function which can box the error if needed.
// t:ts - The error types to convert.
($error:expr, $msg:expr, $bt:expr, $box:expr, $t:ty, $($ts:ty),+ $(,)?) => {{
let e2 = match $error.downcast::<$t>() {
Ok(e) => return SerializedError {
description: $msg,
backtrace: $bt,
error: Some($box(e)),
},
Err(e) => e,
};
to_serialized_error!(e2, $msg, $bt, $box, $($ts,)*);
}};
}
/// Converts a `SerializedError` back into a real error type.
impl From<SerializedError> for anyhow::Error {
fn from(ser: SerializedError) -> anyhow::Error {
let error = if let Some(error) = ser.error {
error.as_anyhow_error()
} else {
anyhow::Error::msg(ser.description)
};
if ser.backtrace == "<disabled>" {
error
} else {
error.context(format!("Server Error.\nRemote {}", ser.backtrace))
}
}
}
/// Converts any error into a `SerializedError`.
impl From<anyhow::Error> for SerializedError {
fn from(error: anyhow::Error) -> SerializedError {
let msg = error.to_string();
let bt = format!("{:#?}", error.backtrace());
to_serialized_error!(
error,
msg,
bt,
Box::new,
crate::bootstrap::BootstrapError,
crate::bootstrap::LegacyBootstrapError,
crate::bootstrap::LegacyRescueError,
crate::io::console::ConsoleError,
crate::io::emu::EmuError,
crate::io::gpio::GpioError,
crate::io::i2c::I2cError,
crate::io::jtag::JtagError,
crate::io::spi::SpiError,
crate::io::uart::UartError,
crate::transport::TransportError,
crate::transport::proxy::ProxyError,
);
}
}