bootreason/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::Error;
6use fidl_fuchsia_feedback::{LastRebootInfoProviderMarker, RebootReason};
7use fuchsia_component::client::connect_to_protocol_sync;
8use log::{debug, info};
9
10/// Timeout for FIDL calls to LastRebootInfoProvider
11const LRIP_FIDL_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::INFINITE;
12
13/// Get an Android-compatible boot reason suitable to add to the cmdline or bootconfig.
14pub fn get_android_bootreason() -> Result<&'static str, Error> {
15    info!("Converting LastRebootInfo to an android-friendly bootreason.");
16    let reboot_info_proxy = connect_to_protocol_sync::<LastRebootInfoProviderMarker>()?;
17    let deadline = zx::MonotonicInstant::after(LRIP_FIDL_TIMEOUT);
18    let reboot_info = reboot_info_proxy.get(deadline)?;
19
20    match reboot_info.reason {
21        Some(RebootReason::Unknown) => return Ok("reboot,unknown"),
22        Some(RebootReason::Cold) => return Ok("reboot,cold"),
23        Some(RebootReason::BriefPowerLoss) => return Ok("reboot,hard_reset"),
24        Some(RebootReason::Brownout) => return Ok("reboot,undervoltage"),
25        Some(RebootReason::KernelPanic) => return Ok("kernel_panic"),
26        Some(RebootReason::SystemOutOfMemory) => return Ok("kernel_panic,oom"),
27        Some(RebootReason::HardwareWatchdogTimeout) => return Ok("watchdog"),
28        Some(RebootReason::SoftwareWatchdogTimeout) => return Ok("watchdog,sw"),
29        Some(RebootReason::RootJobTermination) => return Ok("kernel_panic"),
30        Some(RebootReason::UserRequest) => return Ok("reboot,userrequested"),
31        Some(RebootReason::DeveloperRequest) => return Ok("reboot,shell"),
32        Some(RebootReason::RetrySystemUpdate) => return Ok("reboot,ota"),
33        Some(RebootReason::HighTemperature) => return Ok("shutdown,thermal"),
34        Some(RebootReason::SessionFailure) => return Ok("kernel_panic"),
35        Some(RebootReason::SysmgrFailure) => return Ok("kernel_panic"),
36        Some(RebootReason::FactoryDataReset) => return Ok("reboot,factory_reset"),
37        Some(RebootReason::CriticalComponentFailure) => return Ok("kernel_panic"),
38        Some(RebootReason::ZbiSwap) => return Ok("reboot,normal"),
39        Some(RebootReason::SystemUpdate) => return Ok("reboot,ota"),
40        Some(RebootReason::NetstackMigration) => return Ok("reboot,normal"),
41        Some(RebootReason::AndroidUnexpectedReason) => return Ok("reboot,normal"),
42        Some(RebootReason::AndroidRescueParty) => return Ok("reboot,rescueparty"),
43        Some(RebootReason::AndroidCriticalProcessFailure) => return Ok("reboot,userspace_failed"),
44        Some(RebootReason::__SourceBreaking { .. }) => return Ok("reboot,normal"),
45        None => return Ok("reboot,unknown"),
46    }
47}
48
49/// Get the last reboot reason code.
50fn get_reboot_reason() -> Option<u16> {
51    let reboot_info_proxy = connect_to_protocol_sync::<LastRebootInfoProviderMarker>().ok();
52    let deadline = zx::MonotonicInstant::after(LRIP_FIDL_TIMEOUT);
53    let reboot_info = reboot_info_proxy?.get(deadline);
54    match reboot_info {
55        Ok(info) => match info.reason {
56            Some(r) => Some(r.into_primitive()),
57            None => {
58                info!("Failed to get the reboot reason.");
59                Some(RebootReason::unknown().into_primitive())
60            }
61        },
62        Err(e) => {
63            info!("Failed to get the reboot info: {:?}", e);
64            Some(RebootReason::unknown().into_primitive())
65        }
66    }
67}
68
69/// Get contents for the pstore/console-ramoops* file.
70///
71/// In Linux it contains a limited amount of some of the previous boot's kernel logs.
72/// The ramoops won't be created after a normal reboot.
73pub fn get_console_ramoops() -> Option<Vec<u8>> {
74    debug!("Getting console-ramoops contents");
75    match get_android_bootreason() {
76        Ok(reason) => match reason {
77            "kernel_panic" | "watchdog" | "watchdog,sw" => {
78                Some(format!("Last Reboot Reason: {}\n", get_reboot_reason()?).as_bytes().to_vec())
79                // TODO: Log additional crash signature.
80            }
81            _ => None,
82        },
83        Err(e) => {
84            info!("Failed to get android bootreason for console_ramoops: {:?}", e);
85            None
86        }
87    }
88}