blob: ea2169f7bcc001d20d9b3f8138057dce50234849 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_INCLUDE_PLATFORM_CRASHLOG_H_
#define ZIRCON_KERNEL_INCLUDE_PLATFORM_CRASHLOG_H_
#include <stdio.h>
#include <sys/types.h>
#include <zircon/boot/crash-reason.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <ktl/atomic.h>
#include <ktl/span.h>
class PlatformCrashlog {
public:
// This is common interface definition for specific implementations of crashlog support.
// Depending on the platform, and the resources provided to it, users will end up with
//
// 1) A RAM mappable crashlog implementation, where the crashlog RAM is mapped
// directly accessible by the CPUs from a mapped virtual address.
// 2) An EFI capsule based implementation, where crashlogs get stored in an
// EFI capsule during a non-spontaneous crash.
// 3) A trivial, no-op implementation, because the kernel was not provided any
// way to store data which could survive a reboot.
//
// Generally speaking, the underlying implementation are not multi-thread
// safe, and users should take care to never do something like having multiple
// threads calling into Finalize at the same time.
class Interface {
public:
// Returns a span which points to the region which the kernel should render
// a crashlog payload to during a non-spontaneous crash.
virtual ktl::span<char> GetRenderTarget() = 0;
// Finalize a crashlog just before triggering a reboot. |reason| is the SW
// reboot reason which will be stored in the crashlog header, while |amt| is
// the amount of the render target (see |GetRenderTarget|) which was filled
// before calling finalize.
virtual void Finalize(zircon_crash_reason_t reason, size_t amt) = 0;
// Attempt to recover any crashlog from platform specific storage, writing
// the results to |tgt|. Returns the number of bytes written to |tgt| in
// the process. Users may pass nullptr to |tgt| if they wish to simply
// measure the size of the crashlog to be recovered.
virtual size_t Recover(FILE* tgt) = 0;
// Enable periodic updates of the uptime estimate in the crashlog header.
// This allows systems with directly mappable crashlog RAM to constantly be
// stashing a valid header with an uptime estimate and a reboot reason of
// "UNKNOWN" to be recovered in the case of a spontaneous reboot.
virtual void EnableCrashlogUptimeUpdates(bool enabled) = 0;
protected:
// No one should ever be destroying an implementation of the crashlog
// interface from an Interface* pointer. Bury the destructor as a
// protected, non-virtual destructor to prevent anyone from ever
// accidentally attempting to do so.
~Interface() = default;
};
// Interface management.
//
// By default, there is always a Crashlog implementation available, and a
// reference to it can be fetched using the |Get| method. At boot, however,
// this will be a default trivial implementation which does nothing. The
// system may switch away from this interface when it discovers usable
// non-volatile storage by calling |Bind|, and passing a reference to a
// specific Crashlog implementation. Note that this implementation must stay
// alive for the entire life of the kernel. Once bound to a non-trivial
// implementation, the interface can no longer be rebound, and any attempts to
// do so will return an error.
//
// Code may check to see if a non-trivial implementation has been bound by
// calling |HasNonTrivialImpl|.
static Interface& Get() { return *(interface_.load(ktl::memory_order_acquire)); }
static bool HasNonTrivialImpl() {
return interface_.load(ktl::memory_order_acquire) != &trivial_impl_;
}
static zx_status_t Bind(Interface& impl) {
Interface* expected = &trivial_impl_;
return interface_.compare_exchange_strong(expected, &impl, ktl::memory_order_acq_rel)
? ZX_OK
: ZX_ERR_ALREADY_BOUND;
}
private:
class TrivialImpl : public Interface {
public:
constexpr TrivialImpl() = default;
~TrivialImpl() = default;
ktl::span<char> GetRenderTarget() final { return {}; }
void Finalize(zircon_crash_reason_t reason, size_t amt) final {}
size_t Recover(FILE* tgt) final { return 0; }
void EnableCrashlogUptimeUpdates(bool enabled) final {}
};
// No one should be creating or destroying any PlatformCrashlog instances.
// Everything should be going through a PlatformCrashlog::Interface instead.
PlatformCrashlog() = default;
~PlatformCrashlog() = default;
// The singleton interface. This can be set to a non-trivial implementation
// only once via Bind.
static inline TrivialImpl trivial_impl_;
static inline ktl::atomic<Interface*> interface_{&trivial_impl_};
};
void platform_set_ram_crashlog_location(paddr_t phys, size_t len);
bool platform_has_ram_crashlog();
/* Stash the crashlog somewhere platform-specific that allows
* for recovery after reboot. This will only be called out
* of the panic() handling path on the way to reboot, and is
* not necessarily safe to be called from any other state.
*
* Calling with a NULL log returns the maximum supported size.
* It is safe to query the size at any time after boot. If the
* return is 0, no crashlog recovery is supported.
*/
extern void (*platform_stow_crashlog)(zircon_crash_reason_t reason, const void* log, size_t len);
/* Recover the crashlog, fprintf'ing its contents into the FILE |tgt|
* provided by the caller, then return the length of the recovered
* crashlog.
*
* It is safe to call this function more than once. Users may compute
* the length of the crashlog without rendering it by passing nullptr
* for |tgt|. The length of the rendered log is guaranteed to stay
* constant between calls.
*
*/
extern size_t (*platform_recover_crashlog)(FILE* tgt);
/* Either enable or disable periodic updates of the crashlog uptime. */
extern void (*platform_enable_crashlog_uptime_updates)(bool enabled);
#endif // ZIRCON_KERNEL_INCLUDE_PLATFORM_CRASHLOG_H_