text: // Copyright 2019 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_OBJECT_INCLUDE_OBJECT_EXCEPTION_DISPATCHER_H_
  #define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_EXCEPTION_DISPATCHER_H_
  
  #include <zircon/rights.h>
  #include <zircon/syscalls/exception.h>
  #include <zircon/types.h>
  
  #include <arch/exception.h>
  #include <fbl/ref_ptr.h>
  #include <kernel/event.h>
  #include <object/dispatcher.h>
  #include <object/thread_dispatcher.h>
  
  // Zircon channel-based exception handling uses two primary classes,
  // ExceptionDispatcher (this file) and Exceptionate (exceptionate.h).
  //
  // An ExceptionDispatcher represents a single currently-active exception. This
  // will be transmitted to registered exception handlers in userspace and
  // provides them with exception state and control functionality.
  //
  // An Exceptionate wraps a channel endpoint to help with sending exceptions to
  // userspace. It is a kernel-internal helper class and not exposed to userspace.
  
  class ExceptionDispatcher final
      : public SoloDispatcher<ExceptionDispatcher, ZX_DEFAULT_EXCEPTION_RIGHTS> {
   public:
    // Returns nullptr on memory allocation failure.
    static fbl::RefPtr<ExceptionDispatcher> Create(fbl::RefPtr<ThreadDispatcher> thread,
                                                   zx_excp_type_t exception_type,
                                                   const zx_exception_report_t* report,
                                                   const arch_exception_context_t* arch_context);
  
    static zx_exception_report_t BuildArchReport(uint32_t type,
                                                 const arch_exception_context_t& arch_context);
  
    ~ExceptionDispatcher() final;
  
    zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_EXCEPTION; }
  
    // Marks the current exception handler as done.
    //
    // Once a handle has been created around this object, either
    // WaitForHandleClose() or DiscardHandleClose() must be called to reset
    // our state for the next handler.
    void on_zero_handles() final;
  
    fbl::RefPtr<ThreadDispatcher> thread() const { return thread_; }
    zx_excp_type_t exception_type() const { return exception_type_; }
  
    // Copies the exception report provided at ExceptionDispatcher creation.
    //
    // The exception report is only available while the exception thread is
    // still alive.
    //
    // Returns false and leaves |report| untouched if the thread has died.
    bool FillReport(zx_exception_report_t* report) const;
  
    // Sets the task rights to use for subsequent handle creation.
    //
    // rights == 0 indicates that the current exception handler is not allowed
    // to access the corresponding task handle, for example a thread-level
    // handler cannot access its parent process handle.
    //
    // This must only be called by an Exceptionate before transmitting the
    // exception - we don't ever want to be changing task rights while the
    // exception is out in userspace.
    void SetTaskRights(zx_rights_t thread_rights, zx_rights_t process_rights);
  
    // Creates new thread or process handles.
    //
    // Returns:
    //   ZX_OK on success.
    //   ZX_ERR_ACCESS_DENIED if the task rights have been set to 0.
    //   ZX_ERR_NO_MEMORY if the Handle failed to allocate.
    zx_status_t MakeThreadHandle(HandleOwner* handle) const;
    zx_status_t MakeProcessHandle(HandleOwner* handle) const;
  
    // Whether to resume the thread on exception close or pass it to the
    // next handler in line.
    bool ResumesThreadOnClose() const;
    void SetWhetherResumesThreadOnClose(bool resume_on_close);
  
    // Whether a debugger should have a second chance to handle the exception
    // after the process handler has tried and failed to do so.
    bool IsSecondChance() const;
    void SetWhetherSecondChance(bool second_chance);
  
    // Blocks until the exception handler is done processing.
    //
    // This must be called exactly once every time this exception is
    // successfully sent out to userspace, in order to wait for the response
    // and reset the internal state.
    //
    // Returns:
    //   ZX_OK if the exception was handled and the thread should resume.
    //   ZX_ERR_NEXT if the exception should be passed to the next handler.
    //   ZX_ERR_INTERNAL_INTR_KILLED if the thread was killed.
    zx_status_t WaitForHandleClose();
  
    // Resets the exception state for the next handler.
    //
    // This must be called instead of WaitForHandleClose() if a handle is
    // created around this exception but fails to make it out to userspace,
    // in order to reset the internal state.
    void DiscardHandleClose();
  
    // Wipe out exception state, which indicates the thread has died.
    void Clear();
  
   private:
    ExceptionDispatcher(fbl::RefPtr<ThreadDispatcher> thread, zx_excp_type_t exception_type,
                        const zx_exception_report_t* report,
                        const arch_exception_context_t* arch_context);
  
    // These are const and only set during construction, so don't need to be
    // guarded with get_lock().
    const fbl::RefPtr<ThreadDispatcher> thread_;
    const zx_excp_type_t exception_type_;
  
    // These gets updated by the Exceptionate whenever we get transmitted,
    // according to the rights that specific Exceptionate was registered with.
    zx_rights_t thread_rights_ TA_GUARDED(get_lock()) = 0;
    zx_rights_t process_rights_ TA_GUARDED(get_lock()) = 0;
  
    // These will be nulled out if the underlying thread is killed while
    // userspace still has access to this exception.
    const zx_exception_report_t* report_ TA_GUARDED(get_lock());
    const arch_exception_context_t* arch_context_ TA_GUARDED(get_lock());
  
    bool resume_on_close_ TA_GUARDED(get_lock()) = false;
    bool second_chance_ TA_GUARDED(get_lock()) = false;
    AutounsignalEvent response_event_;
  };
  
  #endif  // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_EXCEPTION_DISPATCHER_H_
  