|  | // 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_ |