| // Copyright 2017 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 |
| |
| #pragma once |
| |
| #include <stdint.h> |
| |
| #include <arch/exception.h> |
| #include <kernel/mutex.h> |
| #include <object/dispatcher.h> |
| |
| #include <zircon/syscalls/exception.h> |
| #include <zircon/syscalls/port.h> |
| #include <zircon/types.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/canary.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/mutex.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| |
| class ThreadDispatcher; |
| class ProcessDispatcher; |
| class PortDispatcher; |
| |
| // Represents the binding of an exception port to a specific target |
| // (system/process/thread). Multiple ExceptionPorts may exist for a |
| // single underlying PortDispatcher. |
| class ExceptionPort : public fbl::DoublyLinkedListable<fbl::RefPtr<ExceptionPort>> |
| , public fbl::RefCounted<ExceptionPort> { |
| public: |
| enum class Type { NONE, DEBUGGER, THREAD, PROCESS, JOB}; |
| |
| static zx_status_t Create(Type type, fbl::RefPtr<PortDispatcher> port, |
| uint64_t port_key, |
| fbl::RefPtr<ExceptionPort>* eport); |
| ~ExceptionPort(); |
| |
| Type type() const { return type_; } |
| |
| zx_status_t SendPacket(ThreadDispatcher* thread, uint32_t type); |
| |
| void OnThreadStart(ThreadDispatcher* thread); |
| |
| void OnThreadSuspending(ThreadDispatcher* thread); |
| void OnThreadResuming(ThreadDispatcher* thread); |
| |
| void OnProcessExit(ProcessDispatcher* process); |
| void OnThreadExit(ThreadDispatcher* thread); |
| void OnThreadExitForDebugger(ThreadDispatcher* thread); |
| |
| // Records the target that the ExceptionPort is bound to, so it can |
| // unbind when the underlying PortDispatcher dies. |
| void SetTarget(const fbl::RefPtr<JobDispatcher>& target); |
| void SetTarget(const fbl::RefPtr<ProcessDispatcher>& target); |
| void SetTarget(const fbl::RefPtr<ThreadDispatcher>& target); |
| |
| // Drops the ExceptionPort's references to its target and PortDispatcher. |
| // Called by the target when the port is explicitly unbound. |
| void OnTargetUnbind(); |
| |
| static void BuildArchReport(zx_exception_report_t* report, uint32_t type, |
| const arch_exception_context_t* arch_context); |
| |
| private: |
| friend class PortDispatcher; |
| |
| ExceptionPort(Type type, fbl::RefPtr<PortDispatcher> port, uint64_t port_key); |
| |
| ExceptionPort(const ExceptionPort&) = delete; |
| ExceptionPort& operator=(const ExceptionPort&) = delete; |
| |
| zx_status_t SendPacketWorker(uint32_t type, zx_koid_t pid, zx_koid_t tid); |
| |
| // Unbinds from the target if bound, and drops the ref to |port_|. |
| // Called by |port_| when it reaches zero handles. |
| void OnPortZeroHandles(); |
| |
| #if DEBUG_ASSERT_IMPLEMENTED |
| // Lets PortDispatcher assert that this eport is associated |
| // with the right instance. |
| bool PortMatches(const PortDispatcher *port, bool allow_null) { |
| fbl::AutoLock lock(&lock_); |
| return (allow_null && port_ == nullptr) || port_.get() == port; |
| } |
| #endif // if DEBUG_ASSERT_IMPLEMENTED |
| |
| // Returns true if the ExceptionPort is currently bound to a target. |
| bool IsBoundLocked() const TA_REQ(lock_) { |
| return target_ != nullptr; |
| } |
| |
| static void BuildReport(zx_exception_report_t* report, uint32_t type); |
| |
| fbl::Canary<fbl::magic("EXCP")> canary_; |
| |
| // These aren't locked as once the exception port is created these are |
| // immutable (the port itself has its own locking though). |
| const Type type_; |
| const uint64_t port_key_; |
| |
| // The underlying port. If null, the ExceptionPort has been unbound. |
| fbl::RefPtr<PortDispatcher> port_ TA_GUARDED(lock_); |
| |
| // The target of the exception port. |
| // The system exception port doesn't have a Dispatcher, hence the bool. |
| fbl::RefPtr<Dispatcher> target_ TA_GUARDED(lock_); |
| |
| fbl::Mutex lock_; |
| |
| // NOTE: The DoublyLinkedListNodeState is guarded by |port_|'s lock, |
| // and should only be touched using port_->LinkExceptionPort() |
| // or port_->UnlinkExceptionPort(). This goes for ::InContainer(), too. |
| }; |
| |
| // Sets the system exception port. |eport| must be non-null; use |
| // ResetSystemExceptionPort() to remove the currently-set port. |
| zx_status_t SetSystemExceptionPort(fbl::RefPtr<ExceptionPort> eport); |
| |
| // Removes the system exception port. Returns true if a port had been set. |
| bool ResetSystemExceptionPort(); |