/*********************************************************************
 *  ASTRIS - Remote JTAG probe
 *
 *  Astris generic SOC abstraction		File: astris_soc.h
 *
 *  This file provides the abstraction for a debuggable SOC
 *
 *********************************************************************
 *
 *  Copyright (C) 2012-2020  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_SOC_H
#define _ASTRIS_SOC_H

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

#include <list>
#include <map>
#include <string>
#include <vector>

#include "astris_api.h"
#include "astris_cpu.h"
#include "armdap.h"

//
// CPU_t - Declare a forward reference to the CPU_t class so that our SOC_t
// class can declare a list of pointers to CPU_t objects.
//
class CPU_t;
class AppleAcc_t;
class AppleAdvancedCore_t;
struct cpu_desc_t;
typedef AXtargetos_t astris_target_os_t;

//
// SOC_t - abstract class capturing the basic API for SOC interaction
//
class SOC_t {
public:
    static SOC_t *createSOC(AXchip_t *chip);

    SOC_t(AXchip_t *chip, int aptype);
    virtual ~SOC_t();

    int  init();
    int  postinit();
    bool cleanup(bool lost_target);

    AXap_t   *sysbus_ap() {return _sysap;}
    AXconn_t *get_conn()  {return _conn;}

    virtual const char *get_name() = 0;

    virtual const char *get_gpu_name() {return NULL; /* default to NULL since not all SoCs have GPUs */}

    virtual const char *get_rev()      {return NULL;}
    int                 get_identity() {return _id;}

    virtual const char *get_collateral_name();

    virtual bool is_normal_memory(AXcpu_t *cpu, AXmemaddr_t addr, AXmemaddr_t len) = 0;

    virtual int xnu_coredump_info(AXmemaddr_t *base_addr, AXmemaddr_t *max_len, AXmemaddr_t *base_addr_sram, AXmemaddr_t *max_len_sram, bool *is_arm64, bool *sys_snoop_cpu)
    {
        return ASTRIS_ERR_NOTSUPPORTED;
    }

    virtual AXmemaddr_t consistent_debug_root_register() {return 0x0;}

    virtual int reinit_if_reset();

    virtual int tckrate_check(uint32_t proposed_tckrate, uint32_t *checked_tckrate);

    virtual int is_stop_clocked();

    /*!
     @function ap_wd_status
     @brief Check status of AP watchdog
     @param[in,out] expired true if the AP watchdog has expired
     @return Astris Return Code
     */
    virtual int ap_wd_status(bool &expired) {expired = false; return ASTRIS_ERR_OK;}

    /*!
     @function smc_slp_status
     @brief  Check for SMC SLP (Sys Low Power) Panic
     @param[in,out] is_panic true if SLP_SMC panic occured, false otherwise
     @return Astris Return Code
     */
    virtual int smc_slp_status(bool &is_panic) {is_panic = false; return ASTRIS_ERR_OK;}

    /*!
     @function disable_watchdog
     @brief  Interface to disable the various SMC-hosted watchdog timers
     @param[in] wd_select enum to indicate which watchdog to disable (enable is not supported)
     @return Astris Return Code
     */
    virtual int disable_watchdog(int wd_select) {return ASTRIS_ERR_OK;}

    virtual int is_kis_supported() {return ASTRIS_ERR_NOTSUPPORTED;}

    /*
        These two functions can be overriden by other SoCs and will be called
        before and after enabling/disabling KIS. However, future SoCs probably
        wont have to do anything.
    */
    virtual int enable_kis_power(bool force_wake) {return ASTRIS_ERR_OK;}
    virtual int disable_kis_power()               {return ASTRIS_ERR_OK;}

    virtual int platform_get_board_id(uint32_t *board_id)
    {
        if (board_id) {*board_id = 0xFFFFFFFF;}
        return ASTRIS_ERR_NOTSUPPORTED;
    }

    virtual int platform_get_sdom(uint8_t *sdom){
        if (sdom) {*sdom = K_DEFAULT_SDOM_VALUE;}
        return ASTRIS_ERR_NOBOARDINFO;
    }
    static constexpr uint8_t K_DEFAULT_SDOM_VALUE = 1;

    // Return the type of OS running on the device
    virtual int get_target_os(astris_target_os_t *os)
    {
        *os = ASTRIS_TARGET_OS_OTHER;
        return ASTRIS_ERR_NOTSUPPORTED;
    }

    // rdar://82736777 (Failure to Surface Panic data for iBoot Panics on Legacy SoC's)
    // Legacy SoC have two stage iOS boot (b) at https://confluence.sd.apple.com/display/CBU/Grand+list+of+boot+stage+transitions
    // Newer SoC have single stage iOS boot (a)
    // Adding this function to differentiate panic checking for those older SoC
    virtual bool has_two_stage_iOS_boot() {return false;}

    virtual int platform_dfu()      {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int platform_dfu_spmi() {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int platform_dfu_eam()  {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int platform_dfu_dcm()  {return ASTRIS_ERR_NOTSUPPORTED;}

    /*!
     @function spmi_read
     @brief Perform an SPMI Read transaction on the SoC
     @param[in] dev_addr Address of the SPMI device
     @param[in] reg_addr Register Address to access
     @param[in,out] value returned bytes from read transaction
     @param[in] count number of bytes to read
     @return Astris Return Code
     */
    virtual int spmi_read(astris_spmi_dev_addr_t dev_addr, astris_spmi_reg_addr_t reg_addr, uint64_t *value, uint8_t count) {return ASTRIS_ERR_NOTSUPPORTED;}
    /*!
     @function spmi_write
     @brief Perform an SPMI Write transaction on the SoC
     @param[in] dev_addr Address of the SPMI device
     @param[in] reg_addr Register Address to access
     @param[in,out] value data to write to register
     @param[in] count number of bytes to write
     @return Astris Return Code
     */
    virtual int spmi_write(astris_spmi_dev_addr_t dev_addr, astris_spmi_reg_addr_t reg_addr, uint64_t value, uint8_t count) {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int set_spmi_interface(uint8_t bus_sel) {return ASTRIS_ERR_NOTSUPPORTED;}

    typedef enum {
        AP_PANIC    = 0,
        SMC_PANIC   = 1,
        AP_WATCHDOG = 2,
    } nmi_type_t;

    virtual int platform_nmi(nmi_type_t nmi) {return ASTRIS_ERR_NOTSUPPORTED;}

    /*!
     @function get_socd_buffer
     @discussion Malloc a buffer of size bytes, read SOCD info from PMU SRAM over SPMI into buffer. Caller is responsible for freeing *socd buffer
     @note array sizes are SOCD_MAX_CONTAINERS
     @param[in,out] socd array pointers to fill in
     @param[in,out] socd_size array of SOCD info buffer sizes in bytes
     @return Astris return code
     */
    virtual int get_socd_buffer(char *socd[], uint32_t socd_size[]) {socd[0] = NULL; socd_size[0] = 0; return ASTRIS_ERR_NOTSUPPORTED;}

    /*!
     @function get_socd_info
     @discussion Access SOCD header region0 (PMU SRAM) to read magic word and size
     @param[in,out] valid True if SOCD magic word indicates a valid SOCD region
     @param[in,out] socd_size SOCD region size in bytes read from SOCD header
     @return Astris return code
     */
    virtual int get_socd_info(bool valid[], uint32_t socd_size[]) {valid[0] = false; socd_size[0] = 0; return ASTRIS_ERR_NOTSUPPORTED;}

    /*!
     @function invalidate_socd_info
     @discussion Write to PMU SOCD header to invalidate the SOCD magic word
     @return Astris return code
     */
    virtual int invalidate_socd_info() {return ASTRIS_ERR_NOTSUPPORTED;}

    /*!
     @function smb_read
     @brief Perform an SMBus (I2C) Read transaction on the SoC, filling in data
     @discussion    Read a sequence of bytes on the SMBus from the requested device.
                    Since some devices (EEPROM, for instance) need a register address
                    (1 or 2 bytes supported; optional) to be set using a "Dummy Write"
                    that preceeds the Read command word after a Repeated START
                    condition on the bus (i.e. no STOP condition after setting the addr),
                    the API function needs to be told whether to perform no Dummy Write,
                    a 1-byte, or a 2-byte Dummy Write prior to the Read command.
                    The caller has to know what the device expects. The read command
                    will be followed by a STOP.
     @param[in] bus Which set of SMB registers to use
     @param[in] dev_addr Address of the SMB device (in bits [7:1])
     @param[in] reg_addr Register Address to access (0 if unused)
     @param[in] addr_len Number of bytes of register address for "Dummy Write" command
     @param[in,out] data returned bytes from read transaction
     @param[in] count number of bytes to read (returns number read successfully
     @return Astris Return Code
     */
    virtual int smb_read(uint8_t bus, uint32_t dev_addr, uint32_t reg_addr, uint8_t addr_len, uint8_t *data, size_t *count) {return ASTRIS_ERR_NOTSUPPORTED;}

    /*!
     @function smb_write
     @brief Perform an SMBus (I2C) Write transaction on the SoC
     @discussion    Write a sequence of bytes on the SMBus to the requested device.
                    It is peripheral-dependent how they are interpreted - i.e. the first 1 or 2
                    bytes may be used as a register offset (EEPROM, for instance). Or not.
                    The caller has to know what the device expects. The API function
                    just transmits the requested number of bytes, followed by a STOP.
     @param[in] bus Which set of SMB registers to use
     @param[in] dev_addr Address of the SMB device (in bits [7:1])
     @param[in,out] data Bytes to transmit
     @param[in] count number of bytes to write (returns number written successfully)
     @return Astris Return Code
     */
    virtual int smb_write(uint8_t bus, uint32_t dev_addr, uint8_t *data, size_t *count) {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int cpu_readreg(AXcpu_t *cpu, AXregid_t regno, AXregval_t *regval);
    virtual int cpu_writereg(AXcpu_t *cpu, AXregid_t regno, AXregval_t regval);
    virtual int cpu_readreg_vals(AXcpu_t *cpu, AXregid_t regno, uint8_t *buffer, uint32_t *reg_size);
    virtual int cpu_writereg_vals(AXcpu_t *cpu, AXregid_t regno, uint8_t *buffer, uint32_t *reg_size);
    virtual int cpu_status(AXcpu_t *cpu, AXcpu_status_t *detailed);
    virtual int cpu_halt(AXcpu_t *cpu);
    virtual int cpu_go(AXcpu_t *cpu);
    virtual int cpu_resume(AXcpu_t *cpu);
    virtual int cpu_sstep(AXcpu_t *cpu);
    virtual int cpu_translate(AXcpu_t *cpu, AXmemaddr_t va, AXmemaddr_t *pa, bool write, int *result);
    virtual int cpu_translate_par(AXcpu_t *cpu, AXmemaddr_t va, AXregval_t *par, bool write, int *result);
    virtual int cpu_translate_with_stage(AXcpu_t *cpu, AXmemaddr_t va, AXmemaddr_t *pa, bool write, uint8_t stage, int *result);
    virtual int cpu_readmem(AXcpu_t *cpu, AXmemaddr_t address, uint32_t *length, uint8_t *buffer);
    virtual int cpu_writemem(AXcpu_t *cpu, AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int iflush);
    virtual int cpu_writemem(AXcpu_t *cpu, AXmemaddr_t address, uint32_t *length, uint8_t *buffer, int iflush, int width);
    virtual int cpu_flush_icache(AXcpu_t *cpu, AXmemaddr_t address, uint32_t length);
    virtual int cpu_clean_dcache(AXcpu_t *cpu, AXmemaddr_t address, uint32_t length);
    virtual int cpu_cleaninv_dcache(AXcpu_t *cpu, AXmemaddr_t address, uint32_t length);
    virtual int cpu_clean_l2cache(AXcpu_t *cpu, AXmemaddr_t address, uint32_t length);
    virtual int cpu_cleaninv_l2cache(AXcpu_t *cpu, AXmemaddr_t address, uint32_t length);
    virtual int cpu_cache_info(AXcpu_t *cpu, AXcacheinfo_t *info);
    virtual int cpu_cache_info_group0(AXcpu_t *cpu, AXcacheinfo_t *info);

    virtual int cpu_cache_info_group0_level(AXcpu_t *cpu, AXcacheinfo_t *info, AXcpu_cache_level_t cache_level) {return ASTRIS_ERR_OK;}
    virtual int cpu_cache_clean(AXcpu_t *cpu, AXcpu_cache_level_t cache_level);
    virtual int cpu_cache_cleaninv(AXcpu_t *cpu, AXcpu_cache_level_t cache_level);

    virtual int cpu_cache_clean_group0(AXcpu_t *cpu, AXcpu_cache_level_t cache_level)    {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int cpu_cache_cleaninv_group0(AXcpu_t *cpu, AXcpu_cache_level_t cache_level) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int cpu_suspend_translation(AXcpu_t *cpu);
    virtual int cpu_resume_translation(AXcpu_t *cpu);
    virtual int cpu_hwbp_info(AXcpu_t *cpu, int index, bpwp_info_t *info);
    virtual int cpu_hwbp_insert(AXcpu_t *cpu, AXmemaddr_t address);
    virtual int cpu_hwbp_remove(AXcpu_t *cpu, int index);
    virtual int cpu_hwbp_enable(AXcpu_t *cpu, int index);
    virtual int cpu_hwbp_disable(AXcpu_t *cpu, int index);
    virtual int cpu_hwwp_info(AXcpu_t *cpu, int index, bpwp_info_t *info);
    virtual int cpu_hwwp_insert(AXcpu_t *cpu, AXmemaddr_t address, uint32_t len, int rdwr);
    virtual int cpu_hwwp_remove(AXcpu_t *cpu, int index);
    virtual int cpu_hwwp_enable(AXcpu_t *cpu, int index);
    virtual int cpu_hwwp_disable(AXcpu_t *cpu, int index);
    virtual int cpu_swbp_info(AXcpu_t *cpu, int index, bpwp_info_t *info);
    virtual int cpu_swbp_insert(AXcpu_t *cpu, AXmemaddr_t address, int len);
    virtual int cpu_swbp_remove(AXcpu_t *cpu, int index);
    virtual int cpu_getinfo(AXcpu_t *cpu, int whichinfo, uint whichspecific, void *info);
    virtual int cpu_collect(AXcpu_t *cpu, int timeout);
    virtual int cpu_abandon(AXcpu_t *cpu);
    virtual int cpu_getmode(AXcpu_t *cpu, const char **mode, uint32_t cpsr);
    virtual int cpu_read_dcc(AXcpu_t *cpu, int *got_val, uint32_t *val);
    virtual int cpu_lacc_status_insert(AXcpu_t *cpu, int status, uint32_t mask);
    virtual int cpu_lacc_status_remove(AXcpu_t *cpu, int status, uint32_t mask);
    virtual int cpu_lacc_count_insert(AXcpu_t *cpu, uint32_t packet_count);
    virtual int cpu_lacc_count_remove(AXcpu_t *cpu);
    virtual int cpu_lacc_count_clear(AXcpu_t *cpu);
    virtual int cpu_lacc_count_read(AXcpu_t *cpu, uint32_t *count);
    virtual int cpu_lacc_pipe(AXcpu_t *cpu, lacc_bp_type_t bp_type, bool pipe);
    virtual int cpu_lacc_bp_status(AXcpu_t *cpu, lacc_bp_type_t bp_type, uint32_t *status);
    virtual int cpu_lacc_pc_status(AXcpu_t *cpu, pipe_stage_t stage, uint32_t *pc);

    /*!
     @function exploreCPU
     @discussion Explore and create CPU Object
     @param cpu A CPU_t pointer that will after this function is called point to the new CPU object
     @param chip A AXchip_t for this chip, it will be used to find the memap to use based on aptype/apseelect
     @param name name of the CPU
     @param base base address of the CPU
     @param aptype This is aptype descibed in armdap.h
     @param group group ID that the CPU is a part of, or -1 for no group
     @param type_hint a type hint to use
     @param apselect This is apselect descirbed in armdap.h
     @result astris error code , On a good error code cpu should point to a valid CPU_t C++ object
     */
    static int exploreCPU(CPU_t * &cpu, AXchip_t *chip, const char *name, AXmemaddr_t base, int aptype, int group, const char *type_hint = NULL, int apselect = -1);

    /*!
     @function createCPU
     @discussion This function explores, creates and attaches the CPU object to the calling SoC function.
                 This function calls into exploreCPU static function above, then attaches the object to the SoC, and
                 given complex.
     @param name name of the CPU
     @param base base address of the CPU
     @param aptype This is aptype descibed in armdap.h
     @param group group ID that the CPU is a part of, or -1 for no group
     @param type_hint a type hint to use
     @param apselect This is apselect descirbed in armdap.h
     @param complex ACC to add the CPU to
     @param cpuid ID for the ACC object to use for the CPU
     @result astris error code , On a good error code cpu should point to a valid CPU_t C++ object
     */
    int createCPU(const char *name, AXmemaddr_t base, int aptype, int group, const char *type_hint = NULL, int apselect = -1, int complex = -1, int cpuid = -1);

    /*!
     @function createCPUPlaceholder
     @discussion Create a CPU placeholder with the given attributes.
     @param ap primary AP for the CPU
     @param name name of the CPU
     @param base base address of the CPU
     @param type_hint a type hint to use
     @param group group ID that the CPU is a part of, or -1 for no group
     @param complex ACC to add the CPU to
     @param cpuid ID for the ACC object to use for the CPU
     @result astris error code
     */
    int createCPUPlaceholder(const char *name, AXmemaddr_t base, AXap_t *ap, int group, const char *type_hint, int complex = -1, int cpuid = -1);

    int createMETA(const char *name, uint32_t idcode, int j_img);

    /*!
     @function get_cpu_by_name
     @discussion Get the CPU object for the CPU with the given name.
     @param name The name to use for lookup
     @result Pointer to the CPU object
     */
    CPU_t *get_cpu_by_name(std::string name);

    /*!
     @function get_cpu_by_index
     @discussion Get the CPU object for the CPU with the given index.
     @param index Index to use for lookup
     @result Pointer to the CPU object
     */
    CPU_t *get_cpu_by_index(int index);

    /*!
     @function get_cpu_complex_by_index
     @discussion Get the CPU complex object with the given index
     @param index The index to use for lookup
     @result Pointer to the CPU complex object
     */
    AppleAcc_t *get_cpu_complex_by_index(int index);

    /*!
     @function get_cpu_complex_by_name
     @discussion Get the CPU complex object with the given name.
     @param name The name to use for lookup
     @result Pointer to the CPU complex object
     */
    AppleAcc_t *get_cpu_complex_by_name(std::string name);

    AppleAdvancedCore_t *get_dbgwrap_block(AXdbgwrap_block_t dbgwrap_block_name);

    AppleAcc_t *get_cpu_complex_contains_cpu(CPU_t *cpu);

    char *get_cpu_complex_name_contains_cpu(CPU_t *cpu);
    int   is_cpu_in_acc_complex(CPU_t *cpu, bool *in_acc);

    virtual int memap_bus_read(unsigned int select, AXmemaddr_t addr, uint8_t *vals, int size, int *count, unsigned flags);
    virtual int memap_bus_write(unsigned int select, AXmemaddr_t addr, uint8_t *vals, int size, int *count, unsigned flags);

    // TODO: HeaderDoc the crap out of this

    virtual int memap_readvals(AXap_t *ap, AXmemaddr_t bus_addr, uint8_t *buffer, int access_size, uint32_t *num_words, bool fifo);
    virtual int memap_writevals(AXap_t *ap, AXmemaddr_t bus_addr, uint8_t *vals, int access_size, uint32_t *num_words, bool fifo);
    virtual int memap_readmemblock(AXap_t *ap, AXmemaddr_t bus_addr, uint32_t *num_bytes, uint8_t *buffer);
    virtual int memap_writememblock(AXap_t *ap, AXmemaddr_t bus_addr, uint32_t *num_bytes, uint8_t *buffer);
    virtual int memap_read32(AXap_t *ap, AXmemaddr_t bus_addr, uint32_t *buffer);
    virtual int memap_write32(AXap_t *ap, AXmemaddr_t bus_addr, uint32_t val);
    virtual int memap_read64(AXap_t *ap, AXmemaddr_t bus_addr, uint64_t *buffer);
    virtual int memap_write64(AXap_t *ap, AXmemaddr_t bus_addr, uint64_t val);

    virtual int memap_debug_readbuf(AXtgt_t *tgt, AXregaddr_t regaddr, uint32_t *regval, int *nwords);
    virtual int memap_debug_writebuf(AXtgt_t *tgt, AXregaddr_t regaddr, uint32_t *regval, int *nwords);
    virtual int memap_debug_read(AXtgt_t *tgt, AXregaddr_t regaddr, uint32_t *regval);
    virtual int memap_debug_write(AXtgt_t *tgt, AXregaddr_t regaddr, uint32_t regval);

    CPU_t *get_cpu_by_name_and_family(const char *cpu_name, CPUFamily family);

    virtual bool is_applesoc() {return false;}

    void add_cpu(CPU_t *cpu);
    void add_complex(AppleAcc_t *complex);
    void add_dbgwrap_block(AppleAdvancedCore_t *dbgwrap_block);
    virtual int asc_dbgwrap_block_init(AppleAdvancedCore_t *asc_dbgwrap_block) {return ASTRIS_ERR_NOTSUPPORTED;}

    AXchip_t *get_chip() {return _chip;}

    void set_booster(bool is_booster);
    bool is_booster();

    static int setup_booster_soc(SOC_t *soc);
    static int parse_soc_info_from_booster(SOC_t *soc);

    virtual bool should_create_cpu(const struct cpu_desc_t &cpu_desc);

    virtual int check_if_subsystem_present(const char *subsystem) {return true;}

    virtual int attach_ps_register_to_cpu(const char *cpu_name, AXmemaddr_t ps_register) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int attach_ascreg_offset_to_cpu(const char *cpu_name, int offset)            {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int attach_asc_dbgack_to_cpu(const char *cpu_name, AXmemaddr_t dbgack)       {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int attach_dart(Dart *dart)                                                  {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int find_dart_by_address(AXmemaddr_t dart_address, Dart * &dart)             {return ASTRIS_ERR_NOTSUPPORTED;}

    // Moved this method from protected to not protected so that when we creating SoC from boosters
    // we can call it. Regular postinit, first creates CPUs then calls it. We dont need to create cpus
    // we just need to call postinit_hw. Basically no one should use this unless in the setup_soc_booster
    int postinit_hw();

    int addr_in_dram(AXmemaddr_t addr, bool *is_dram);

    virtual int dfu_check(bool *isdfu) {return ASTRIS_ERR_NOTSUPPORTED;}

    virtual int get_gd_bus(int *bus)                     {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int get_boot_cluster(uint32_t *boot_cluster) {return ASTRIS_ERR_NOTSUPPORTED;}

    int set_stop_clock(bool stop_clock_active);

    virtual int read_all_breadcrumbs(char **crumbs_info_string) {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int write_halt_breadcrumb(int cpu_num)              {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int write_hang_breadcrumb(AXmemaddr_t hang_addr)    {return ASTRIS_ERR_NOTSUPPORTED;}
    virtual int clear_all_breadcrumbs()                         {return ASTRIS_ERR_NOTSUPPORTED;}

#if !IS_ASTRIS_LITE_API
    virtual int debug_auth()           {return ASTRIS_ERR_OK;}
    virtual int debug_auth_secondary() {return ASTRIS_ERR_OK;}
#endif

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

    virtual int  _preinit_hw()                                             {return sys_pwr_up();}
    virtual int  _query_soc_rev()                                          {return ASTRIS_ERR_OK;}
    virtual int  _stop_clock_check()                                       {return ASTRIS_ERR_OK;}
    virtual bool _is_dbgwrap_address(uint32_t select, AXmemaddr_t address) {return false;}
    virtual int  _take_gateway_control()                                   {return ASTRIS_ERR_OK;}
    virtual int  _release_gateway_control()                                {return ASTRIS_ERR_OK;}
    virtual int  _stop_clock_workaround()                                  {return ASTRIS_ERR_OK;}
    virtual bool _cleanup_hw()                                             {return true;}
    virtual int  _postinit_hw()                                            {return ASTRIS_ERR_OK;}
    virtual int  _create_cpus()                                            {return ASTRIS_ERR_OK;}
    virtual bool _was_reset()                                              {return false;}
    virtual int  _setup_aps();

    static SOC_t* _create_builtin_soc(AXchip_t *chip);
    static SOC_t* _create_pmu_soc(AXchip_t *chip);

    /*!
     @function _init_objs
     @brief Function used to initialize objects that the SoC needs. This is for the
     objects that might fail to initialize so we don't instantiate them in the constructor.
     @return ASTRIS_ERR code
     */
    virtual int _init_objs() {return ASTRIS_ERR_OK;}

    virtual int _with_cpu_thunk(AXcpu_t * cpu, const char *caller, int (^cpu_thunk)(CPU_t *));

    virtual int _with_preflight_for_bus_transaction(AXap_t * ap, AXmemaddr_t addr, int access_size, uint32_t num_words, int (^do_access)(void));

    int sys_pwr_up(bool wait_for_csyspwrupack = true);

    virtual int _cpu_cache_info_group0_shared_l2(AXcpu_t *cpu, AXcacheinfo_t *info, AXcpu_cache_level_t cache_level);
    virtual int _cpu_cache_clean_group0_l2(AXcpu_t *cpu, AXcpu_cache_level_t cache_level);
    virtual int _cpu_cache_cleaninv_group0_l2(AXcpu_t *cpu, AXcpu_cache_level_t cache_level);

    void override_desc_based_on_soc_rev(struct cpu_desc_t &cpu_desc);
    bool compare_soc_rev(const char *override_rev, const char *soc_rev);
    bool soc_rev_greater_or_equal(const char *soc_rev, char *cmp_rev);
    bool soc_rev_less_than(const char *soc_rev, char *cmp_rev);

    // This is identity, from chip->identity, should map to astris_soc_id
    int _id;

    // Cache pointers to the various C objects needed to get work done
    AXchip_t *_chip;
    AXconn_t *_conn;
    AXap_t *_sysap;
    AXap_t *_dbgap;

    /*!
     @var _cpus
     @discussion List of CPUs in the SoC
     */
    std::list <CPU_t *> _cpus;

    /*!
     @var _complexes
     @discussion List of CPU complexes in the SoC
     */
    std::vector <AppleAcc_t *> _complexes;

    std::vector <AppleAdvancedCore_t *> _dbgwrap_blocks;

    /*!
     @var m_stop_clock_active
     @discussion During explore, we're going to check for the stop clock
     condition. If we detect that condition to be true, we're going to want to
     intervene by preventing any MEM-AP accesses. If we don't do this, there
     are situations in which we could wedge the DAP.  While we don't normally
     like to do things like this without the user's knowledge, it seems
     appropriate in this scenario since the normal debug flow is to immediately
     jebdump.
     */
    bool m_stop_clock_active = false;

    // Provide some standard memory size suffixes to make code in
    // derived classes easier to read.
    static const AXmemaddr_t KiB = 1024;
    static const AXmemaddr_t MiB = 1024 * KiB;
    static const AXmemaddr_t GiB = 1024 * MiB;

    bool _booster = false;

    std::vector <Dart *> _darts;

private:

    bool _initted = false;
    int _sysbus_aptype;

};

class MiscCortex_t:
    public SOC_t {
    int _aptype;
public:
    MiscCortex_t(AXchip_t *chip, int aptype) :
        SOC_t(chip, aptype /*K_APIDR_TYPE_AHB*/),
        _aptype(aptype)
    {}

    virtual const char *get_name() {return "MiscCortex";}

    virtual bool is_normal_memory(AXcpu_t *cpu, AXmemaddr_t addr, AXmemaddr_t len);

protected:
    virtual int _create_cpus();

private:
};

class RaspberryPi3_t:
    public SOC_t {
public:
    RaspberryPi3_t(AXchip_t *chip) :
        SOC_t(chip, K_APIDR_TYPE_APB)
    {}

    virtual const char *get_name() {return "RaspberryPi3_BCM2837";}
    virtual bool        is_normal_memory(AXcpu_t *cpu, AXmemaddr_t addr, AXmemaddr_t len);

protected:
    virtual int _create_cpus();

};
#endif /* _ASTRIS_SOC_H */
