/*      $NetBSD: stackframe.h,v 1.4 2016/07/31 19:33:18 dholland Exp $  */

/*
* Contributed to the NetBSD foundation by Cherry G. Mathew
*/

#define UNW_VER(x)           ((x) >> 48)
#define UNW_FLAG_MASK        0x0000ffff00000000L
#define UNW_FLAG_OSMASK      0x0000f00000000000L
#define UNW_FLAG_EHANDLER(x) ((x) & 0x0000000100000000L)
#define UNW_FLAG_UHANDLER(x) ((x) & 0x0000000200000000L)
#define UNW_LENGTH(x)        ((x) & 0x00000000ffffffffL)

/* Unwind table entry. */
struct uwtable_ent {
       uint64_t start;
       uint64_t end;
       char *infoptr;
};


enum regrecord_type{
       /* Register contents live ( and therefore untouched ). */
       UNSAVED,
       /* .offset field is the saved content. */
       IMMED,
       /* Register saved in one of the Branch Registers. */
       BRREL,
       /*
        * Register saved in one of the Stacked GRs
        * regstate.offset contains GR number (usually >= 32)
        */
       GRREL,
       /*
        * Register saved on the memory stack frame.
        * regstate.offset is in words; ie; location == (sp + 4 * spoff).
        */
       SPREL,
       /*
        * Register saved on the memory stack frame but offseted via psp
        * regstate.offset is in words; ie,
        * location == (psp + 16 �– 4 * pspoff)
        */
       PSPREL
};


struct regstate {
       enum regrecord_type where;
       uint64_t when;

#define INVALID -1UL    /* Indicates uninitialised offset value. */
       uint64_t offset;
};


/* A staterecord contains the net state of
* sequentially parsing unwind descriptors.
* The entry state of the current prologue region
* is the exit state of the previous region.
* We record info about registers we care about
* ie; just enough to re-construct an unwind frame,
* and ignore the rest.
* Initial state is where = UNSAVED for all .where fields.
*/

struct staterecord {
       struct regstate bsp;
       struct regstate psp;
       struct regstate rp;
       struct regstate pfs;
};

/* The unwind frame is a simpler version of the trap frame
* and contains a subset of preserved registers, which are
* useful in unwinding an ia64 stack frame.
* Keep this in sync with the staterecord. See: stackframe.c:updateregs()
*/

struct unwind_frame {
       uint64_t                bsp;    /* Base of the RSE. */
                                   /* !!! XXX: Stack Frame discontinuities */
       uint64_t                psp;    /* Mem stack (variable size) base. */
       uint64_t                rp;     /* Return Pointer */
       uint64_t                pfs;    /* Previous Frame size info */

       /* Don't mirror anything below this line with struct staterecord */
       uint64_t                sp;
};


void buildrecordchain(uint64_t, struct recordchain *);
void initrecord(struct staterecord *);
void modifyrecord(struct staterecord *, struct recordchain *, uint64_t);
void pushrecord(struct staterecord *);
void poprecord(struct staterecord *, int);
void dump_staterecord(struct staterecord *);
void clonerecordstack(u_int);
void switchrecordstack(u_int);

struct uwtable_ent *get_unwind_table_entry(uint64_t);
void patchunwindframe(struct unwind_frame *, uint64_t, uint64_t);
void updateregs(struct unwind_frame *uwf, struct staterecord *, uint64_t);
struct uwtable_ent * get_unwind_table_entry(uint64_t ip);

struct staterecord *buildrecordstack(struct recordchain *, uint64_t);
void dump_recordchain(struct recordchain *);

/* Convenience macros to decompose CFM & ar.pfs. */
#define IA64_CFM_SOF(x)         ((x) & 0x7f)
#define IA64_CFM_SOL(x)         (((x) >> 7) & 0x7f)
#define IA64_CFM_SOR(x)         (((x) >> 14) & 0x0f)
#define IA64_CFM_RRB_GR(x)      (((x) >> 18) & 0x7f)
#define IA64_CFM_RRB_FR(x)      (((x) >> 25) & 0x7f)
#define IA64_CFM_RRB_PR(x)      (((x) >> 32) & 0x3f)

#define IA64_RNATINDEX(x)       (((x) & 0x1f8) >> 3)

/* Obeys Table 6:2 RSE Operation Instructions and State Modification */

/* These functions adjust for RSE rnat saves to bsp in the forward and
* reverse directions respectively.
*/
#define ia64_rnat_adjust ia64_bsp_adjust_call

static __inline uint64_t
ia64_bsp_adjust_call(uint64_t bsp, int sol)
{
       bsp += ((sol + (IA64_RNATINDEX(bsp) + sol) / 63) << 3);
       return bsp;
}

static __inline uint64_t
ia64_bsp_adjust_ret(uint64_t bsp, int sol)
{
       bsp -= ((sol + (62 - IA64_RNATINDEX(bsp) + sol) / 63) << 3);
       return bsp;
}

static __inline uint64_t
ia64_getrse_gr(uint64_t bsp, uint64_t gr)
{
       bsp = ia64_bsp_adjust_call(bsp, gr);
       return *(uint64_t *) bsp;
}