/*  *********************************************************************
    *  ASTRIS - Remote JTAG probe
    *
    *  Astris generic CPU abstraction		File: astris_cpu.h
    *
    *  This file provides the abstraction for a debuggable CPU
    *
    *********************************************************************
    *
    *  Copyright (C) 2009-2017  Apple, Inc. All rights reserved.
    *
    *  This document is the property of Apple Inc.
    *  It is considered confidential and proprietary.
    *
    *  This document may not be reproduced or transmitted in any form,
    *  in whole or in part, without the express written permission of
    *  Apple Inc.
    ********************************************************************* */

#ifndef _ASTRIS_CPU_H
#define _ASTRIS_CPU_H

/* This header covers the interface between the CPU frontend and various backends */

#include "astris_api.h"
#include "astris_arm.h"

class Dart;
class SOC_t;

typedef enum {
    CACHEOP_IC_CLEAN            = 0,
    CACHEOP_IC_SYNC             = 1,
    CACHEOP_DC_CLEAN            = 2,
    CACHEOP_DC_FLUSH            = 3,
    CACHEOP_L2_CLEAN            = 4,
    CACHEOP_L2_CLEAN_INVALIDATE = 5,
} cpu_cacheop_t;

typedef enum CPUFamily_t {
    kCPU = 0,
    kARM32,
    kARMv7m,
    kARM64,
    kAppleARM64,
    kXtensa,
    kArc,
    kLACC,
} CPUFamily;

#define AXBPST_AVAIL    0
#define AXBPST_USED     1
#define AXBPST_RESERVED 2

/* Breakpoint operation macros */
#define AXCPU_BP_INSERT  0
#define AXCPU_BP_REMOVE  1
#define AXCPU_BP_ENABLE  2
#define AXCPU_BP_DISABLE 3
#define AXCPU_BP_GETINFO 4

/* Watchpoint operation macros */
#define AXCPU_WP_INSERT  5
#define AXCPU_WP_REMOVE  6
#define AXCPU_WP_ENABLE  7
#define AXCPU_WP_DISABLE 8
#define AXCPU_WP_GETINFO 9

/* Software breakpoint macros */
#define AXCPU_SW_INSERT  10
#define AXCPU_SW_REMOVE  11
#define AXCPU_SW_GETINFO 12

#define AXCPU_SWBP_DFLT 1024

// AXSPEW equivalent for use by the CPU_t class or any class derived from CPU_t
#define CPUSPEW(args...) ax_os_log(AXLOG_M_CPU, AXL_LVL_LOW, args)

// AXVERIFY_CODE equivalents for use by the CPU_t class or any class derived from CPU_t
#define CPU_VERIFY_CODE(result, fmt, ...) \
    AXVERIFY_CODE(AXLOG_M_CPU, AXL_ERR, result, fmt, ## __VA_ARGS__)

#define CPUSPEW_VERIFY_CODE(result, fmt, ...) \
    AXVERIFY_CODE(AXLOG_M_CPU, AXCONN_FLG_DEBUG_CPU, result, fmt, ## __VA_ARGS__)

// AXREQUIRE_CODE equivalents for use by the CPU_t class or any class derived from CPU_t
#define CPU_REQUIRE_CODE(result, label, fmt, ...) \
    AXREQUIRE_CODE(AXLOG_M_CPU, AXL_ERR, result, label, fmt, ## __VA_ARGS__)

#define CPUSPEW_REQUIRE_CODE(result, label, fmt, ...) \
    AXREQUIRE_CODE(AXLOG_M_CPU, AXCONN_FLG_DEBUG_CPU, result, label, fmt, ## __VA_ARGS__)

//
// CPU_t - abstract class capturing the basic API for CPU control
//
class CPU_t {
public:

    static CPU_t *createCPU(AXtgt_t *tgt, const char *name, dbgarch_t arch, uint32_t id, uint32_t implementer, uint32_t partnum);

    CPU_t(AXtgt_t *tgt, const char *name);
    CPU_t(const char *name); // This constructor is really only here for testing purposes - it bypasses much of the necessary object initialization, so it shouldn't be used when astris is in functional mode.

    virtual ~CPU_t();

    bool init();
    bool cleanup();

    /*
    @function init_obj
    @abstract The init function only gets called AFTER polling. This function can be
    called to further initialize the object before polling can be done. Unlike the
    constructor this function is allowed to fail.
    */
    virtual int init_obj() {return ASTRIS_ERR_OK;}

    AXconn_t           *get_conn();
    virtual const char *get_name();
    virtual int         get_index();
    virtual int         get_info(int whtinfo, unsigned whatspecific, void *info);
    virtual int         status(uint32_t *status, bool no_init_or_collect = false);
    virtual int         status_detailed(AXcpu_status_t *detailed, bool no_init_or_collect);

    virtual int halt();
    virtual int collect(int timeout);
    virtual int resume(bool step = false);
    virtual int go();

    virtual int sstep() {return resume(true);}
	virtual int reverse(bool reverse) {return ASTRIS_ERR_NOTSUPPORTED;} 

    int         set_halting(bool enable) {return _set_halting(enable);}
    virtual int clear_lockacc()          {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int set_lockacc()            {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int read_register(AXregid_t reg, AXregval_t *val)                           {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int write_register(AXregid_t reg, AXregval_t val)                           {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int read_register_vals(AXregid_t reg, uint8_t *buffer, uint32_t *reg_size)  {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int write_register_vals(AXregid_t reg, uint8_t *buffer, uint32_t *reg_size) {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int memory_read(AXmemaddr_t address, uint32_t *length, uint8_t *buffer)                         {*length = 0; return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int memory_write(AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int iflush)            {*length = 0; return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int memory_write(AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int iflush, int width) {*length = 0; return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int hwbp_info(int index, bpwp_info_t *info);
    virtual int hwbp_insert(AXmemaddr_t address);
    virtual int hwbp_remove(int index);
    virtual int hwbp_enable(int index);
    virtual int hwbp_disable(int index);

    virtual int hwwp_info(int index, bpwp_info_t *info);
    virtual int hwwp_insert(AXmemaddr_t address, uint32_t len, int rdwr);
    virtual int hwwp_remove(int index);
    virtual int hwwp_enable(int index);
    virtual int hwwp_disable(int index);

    virtual int swbp_info(int index, bpwp_info_t *info);
    virtual int swbp_insert(AXmemaddr_t address, int len);
    virtual int swbp_remove(int index);

    // LACC specific breakpoint functions
    virtual int hwbp_status_insert(int stat, uint32_t mask)          {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int hwbp_status_remove(int stat, uint32_t mask)          {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int hwbp_count_insert(uint32_t num_packets)              {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int hwbp_count_remove()                                  {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int hwbp_count_clear()                                   {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int get_packet_count(uint32_t *count)                    {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int set_pipe(lacc_bp_type_t bp_type, bool pipe)          {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int get_bp_status(lacc_bp_type_t bp_type, uint32_t *val) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int get_pc_status(pipe_stage_t stage, uint32_t *val)     {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int cache_op(cpu_cacheop_t op, AXmemaddr_t addr, uint32_t length) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int cache_info(AXcacheinfo_t *info)                               {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int cache_clean(AXcpu_cache_level_t cache_level)                  {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int cache_cleaninv(AXcpu_cache_level_t cache_level)               {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int translate(AXmemaddr_t va, AXmemaddr_t *pa, bool write, int *result)                           {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int translate_with_stage(AXmemaddr_t va, AXmemaddr_t *pa, bool write, uint8_t stage, int *result) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int translate_par(AXmemaddr_t va, AXregval_t *par, bool write, int *result)                       {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int r0unmap_and_aslr_translate(AXmemaddr_t va, AXmemaddr_t *translated_va)                        {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int suspend_translation(void);
    virtual int resume_translation(void);

    bool is_collected(void) {return collected;}

    virtual int print_user_regs() {return ASTRIS_ERR_OK;}

    virtual int   set_dart(Dart *dart);
    virtual Dart *get_dart();

    virtual CPUFamily get_cpu_family() {return CPUFamily::kCPU;}

    virtual AXcpu_t *get_ax_cpu();
    void             set_ax_cpu(AXcpu_t *cpu);

    virtual int get_sw_info(AXrtxcid_t *sw_info) {return ASTRIS_ERR_NOTSUPPORTED;}

public:
    // Public methods in this section are specific to ARMcpu_t and subclasses;
    // however, for regularity, the hooks appear on the base CPU_t class.
    virtual int cpu_mode(const char **mode, uint32_t cpsr) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int read_dcc(int *got_val, uint32_t *val)      {return ASTRIS_ERR_NOTSUPPORTED;}

protected:
    const char *_log_key; // CPU logging domain

    bool         get_cpu_initted()                             {return cpu_initted;}
    virtual int  _get_regwidth_hw()                            {return 4;}
    virtual int  _get_type_hw()                                {return AXCPUTYPE_UNKNOWN;}
    virtual int  _get_arch_hw()                                {return AXCPUARCH_UNKNOWN;}
    virtual bool _init_hw()                                    {return true;}
    virtual bool _cleanup_hw()                                 {return true;}
    virtual int  _status_hw(uint32_t *status, uint32_t *flags) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int  _halt_hw()                                    {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int  _collect_hw(int timeout)                      {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int  _resume_hw(bool step)                         {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int  _flush_state_hw()                             {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int  _is_halted(bool *halted)                      {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int _status_hw_detailed(AXcpu_status_t *detailed, uint32_t *flags)
    {
        detailed->pmgr     = AXCPU_PMGR_INVALID;
        detailed->asc      = AXCPU_ASC_INVALID;
        detailed->standard = AXCPU_STATUS_INVALID;
        return _status_hw(&detailed->standard, flags);
    }

    /*!
     @function _set_halting
     @discussion Set halting debug enabled, if the CPU supports such feature.
     @result astris error code, should return ASTRIS_ERR_NOTFOUND if halting bit was not set
             after trying to confirm it set or other error codes for memory transaction fails.
     */
    virtual int _set_halting(bool enable) {return ASTRIS_ERR_OK;}

    //
    // Generic Breakpoint/watchpoint accessors for subclasses
    //
    int _get_bpwp_info(uint32_t index, uint32_t req_info, bpwp_info_t *info);
    int _is_bpwp_initialized();

    int  _get_num_hwbp()        {return bpwp_initted ? m_num_hwbp : 0;}
    int  _get_num_hwwp()        {return bpwp_initted ? m_num_hwwp : 0;}
    bool _is_hwbp_avail(int bp) {return bpwp_initted ? (m_bp_avail & (1 << bp)) : false;}
    bool _is_hwwp_avail(int wp) {return bpwp_initted ? (m_wp_avail & (1 << wp)) : false;}

    //
    // Low-level breakpoint/watchpoint default implementation
    //
    virtual int _get_num_hwbp_hw(int *numbp)                                                       {*numbp = 0; return ASTRIS_ERR_OK;}
    virtual int _get_num_hwwp_hw(int *numwp)                                                       {*numwp = 0; return ASTRIS_ERR_OK;}
    virtual int _set_bpwp(int op, bpwp_info_t *info)                                               {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _setup_bp(bpwp_info_t *info, int *bp_state)                                        {*bp_state = AXBPST_RESERVED; return ASTRIS_ERR_OK;}
    virtual int _setup_wp(bpwp_info_t *info, int *wp_state)                                        {*wp_state = AXBPST_RESERVED; return ASTRIS_ERR_OK;}
    virtual int _insert_breakpoint(bpwp_info_t *info, AXmemaddr_t address)                         {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _remove_breakpoint(int which_bp)                                                   {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _insert_watchpoint(bpwp_info_t *info, AXmemaddr_t address, uint32_t len, int rdwr) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _remove_watchpoint(int which_wp)                                                   {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _software_breakpoint(uint32_t *len, uint32_t *old_data)                            {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _write_executable_memory(bpwp_info_t *bp_info);
    virtual int _cleanup_swbp_hwbp_hwwp();

    //
    // Address translation control default implementation
    //
    virtual int _suspend_translation_hw(void) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _resume_translation_hw(void)  {return ASTRIS_ERR_NOTSUPPORTED;}

    //
    // Memory access control default implementation
    //
    virtual int _suspend_permission_checks(void) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int _resume_permission_checks(void)  {return ASTRIS_ERR_NOTSUPPORTED;}

    //
    // Generic type for holding register values and their state
    //
    typedef struct cpureg_t {
        union {
            AXregval_t reg_val;
            uint32_t   val32;
            uint64_t   val64;
        };
        bool cached;
        bool dirty;
    } cpureg_t;

    // Cache pointers to the various C objects needed to get work done
    AXconn_t *_conn;
    AXchip_t *_chip;
    AXtgt_t *_tgt;
    AXcpu_t *_cpu;
    SOC_t *_soc;
    Dart *_dart;
    bool collected;
    char *cpu_name;

private:

    void _init_bpwp();
    int  _find_bpwp(int want_wp);
    int  _sw_breakpoint_insert(AXmemaddr_t address, uint32_t len);
    int  _sw_breakpoint_remove(int index);

    void _refresh_hw_bpwp();

    int m_num_swbp;
    int m_num_hwbp;
    int m_num_hwwp;
    int m_bp_avail;
    int m_wp_avail;
    int m_bp_rsvd;
    int m_wp_rsvd;
    int m_bp_enabled;
    int m_wp_enabled;
    bpwp_info_t *m_bp_details;
    bpwp_info_t *m_wp_details;
    bpwp_info_t m_sw_details[AXCPU_SWBP_DFLT];

    bool cpu_initted;
    bool bpwp_initted;
    uint32_t last_status;
};

#endif /* _ASTRIS_CPU_H */
