/* Copyright (C) 2021 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

/* Hardware counter profiling driver's header */

#ifndef __HWCDRV_H
#define __HWCDRV_H

#include "hwcfuncs.h"

#ifdef linux
#define HWCFUNCS_SIGNAL         SIGIO
#define HWCFUNCS_SIGNAL_STRING  "SIGIO"
#else
#define HWCFUNCS_SIGNAL         SIGEMT
#define HWCFUNCS_SIGNAL_STRING  "SIGEMT"
#endif

#ifndef LIBCOLLECTOR_SRC /* not running in libcollector */
#include <string.h>

#else /* running in libcollector */
#include "collector_module.h"
#include "libcol_util.h"

#define get_hwcdrv                  __collector_get_hwcdrv
#define hwcdrv_drivers              __collector_hwcdrv_drivers
#define hwcdrv_cpc1_api             __collector_hwcdrv_cpc1_api
#define hwcdrv_cpc2_api             __collector_hwcdrv_cpc2_api
#define hwcdrv_default              __collector_hwcdrv_default
#define hwcdrv_driver               __collector_hwcdrv_driver
#define hwcdrv_init                 __collector_hwcdrv_init
#define hwcdrv_get_info             __collector_hwcdrv_get_info
#define hwcdrv_enable_mt            __collector_hwcdrv_enable_mt
#define hwcdrv_get_descriptions     __collector_hwcdrv_get_descriptions
#define hwcdrv_assign_regnos        __collector_hwcdrv_assign_regnos
#define hwcdrv_create_counters      __collector_hwcdrv_create_counters
#define hwcdrv_start                __collector_hwcdrv_start
#define hwcdrv_overflow             __collector_hwcdrv_overflow
#define hwcdrv_read_events          __collector_hwcdrv_read_events
#define hwcdrv_sighlr_restart       __collector_hwcdrv_sighlr_restart
#define hwcdrv_lwp_suspend          __collector_hwcdrv_lwp_suspend
#define hwcdrv_lwp_resume           __collector_hwcdrv_lwp_resume
#define hwcdrv_free_counters        __collector_hwcdrv_free_counters
#define hwcdrv_lwp_init             __collector_hwcdrv_lwp_init
#define hwcdrv_lwp_fini             __collector_hwcdrv_lwp_fini
#define hwcdrv_assign_all_regnos    __collector_hwcdrv_assign_all_regnos
#define hwcdrv_lookup_cpuver        __collector_hwcdrv_lookup_cpuver
#define hwcfuncs_int_capture_errmsg  __collector_hwcfuncs_int_capture_errmsg

#define GTXT(x) x

/* Implemented by libcollector */
#define calloc          __collector_calloc
#define close           CALL_UTIL(close)
#define fcntl           CALL_UTIL(fcntl)
#define fprintf         CALL_UTIL(fprintf)
//#define free            __collector_free
#define free(...)
#define gethrtime       __collector_gethrtime
#define ioctl           CALL_UTIL(ioctl)
#define malloc          __collector_malloc
#define memcpy          __collector_memcpy
#define memset          CALL_UTIL(memset)
#define mmap            CALL_UTIL(mmap)
#define snprintf        CALL_UTIL(snprintf)
#define strchr          CALL_UTIL(strchr)
#define strcmp          CALL_UTIL(strcmp)
#define strncmp         CALL_UTIL(strncmp)
#define strcpy          CALL_UTIL(strcpy)
#define strdup          __collector_strdup
#define strncpy         CALL_UTIL(strncpy)
#define strerror        CALL_UTIL(strerror)
#define strlen          CALL_UTIL(strlen)
#define strstr          CALL_UTIL(strstr)
#define strtol          CALL_UTIL(strtol)
#define strtoll         CALL_UTIL(strtoll)
#define strtoul         CALL_UTIL(strtoul)
#define strtoull        CALL_UTIL(strtoull)
#define syscall         CALL_UTIL(syscall)
#define sysconf         CALL_UTIL(sysconf)
#define vsnprintf       CALL_UTIL(vsnprintf)

#endif  /* --- LIBCOLLECTOR_SRC --- */

/* TprintfT(<level>,...) definitions.  Adjust per module as needed */
#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
#define DBG_LT1 1 // for configuration details, warnings
#define DBG_LT2 2
#define DBG_LT3 3
#define DBG_LT4 4

#ifdef __cplusplus
extern "C"
{
#endif

  /* hwcdrv api */
  typedef struct
  {
    int (*hwcdrv_init)(hwcfuncs_abort_fn_t abort_ftn, int * tsd_sz);
    /* Initialize hwc counter library (do not call again after fork)
	 Must be called before other functions.
       Input:
	 <abort_ftn>: NULL or callback function to be used for fatal errors
	 <tsd_sz>: If not NULL, returns size in bytes required for thread-specific storage
       Return: 0 if successful
     */

    void (*hwcdrv_get_info)(int *cpuver, const char **cciname, uint_t *npics,
			    const char **docref, uint64_t *support);
    /* get info about session
       Input:
	 <cpuver>: if not NULL, returns value of CPC cpu version
	 <cciname>: if not NULL, returns name of CPU
	 <npics>: if not NULL, returns maximum # of HWCs
	 <docref>: if not NULL, returns documentation reference
	 <support>: if not NULL, returns bitmask (see hwcfuncs.h) of hwc support
       Return: 0 if successful, nonzero otherwise
     */

    int (*hwcdrv_enable_mt)(hwcfuncs_tsd_get_fn_t tsd_ftn);
    /* Enables multi-threaded mode (do not need to call again after fork)
       Input:
	 <tsd_ftn>: If <tsd_sz>==0, this parameter is ignored.
		    Otherwise:
		     tsd_ftn() must be able to return a pointer to thread-specific
		     memory of <tsd_sz> bytes.
		     For a given thread, tsd_ftn() must
		     always return the same pointer.
       Return: none
     */

    int (*hwcdrv_get_descriptions)(hwcf_hwc_cb_t *hwc_find_action,
				   hwcf_attr_cb_t *attr_find_action);
    /* Initiate callbacks with all available HWC names and and HWC attributes.
       Input:
	 <hwc_find_action>: if not NULL, will be called once for each HWC
	 <attr_find_action>: if not NULL, will be called once for each attribute
       Return: 0 if successful
	  or a cpc return code upon error
     */

    int (*hwcdrv_assign_regnos)(Hwcentry* entries[], unsigned numctrs);
    /* Assign entries[]->reg_num values as needed by platform
       Input:
	 <entries>: array of counters
	 <numctrs>: number of items in <entries>
       Return: 0 if successful
	  HWCFUNCS_ERROR_HWCINIT if resources unavailable
	  HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly
     */

    int (*hwcdrv_create_counters)(unsigned hwcdef_cnt, Hwcentry *hwcdef);
    /* Create the counters, but don't start them.
	 call this once in main thread to create counters.
       Input:
	 <defcnt>: number of counter definitions.
	 <hwcdef>: counter definitions.
       Return: 0 if successful
	  or a cpc return code upon error
     */

    int (*hwcdrv_start)(void);
    /* Start the counters.
	 call this once in main thread to start counters.
       Return: 0 if successful
	  or a cpc return code upon error
     */

    int (*hwcdrv_overflow)(siginfo_t *si, hwc_event_t *sample,
			   hwc_event_t *lost_samples);
    /* Linux only.  Capture current counter values.
	 This is intended to be called from SIGEMT handler;
       Input:
	 <si>: signal handler context information
	 <sample>: returns non-zero values for counters that overflowed
	 <lost_samples>: returns non-zero values for counters that "lost" counts
       Return: 0 if successful
	  or a cpc return code upon error.
     */

    int (*hwcdrv_read_events)(hwc_event_t *overflow_data,
			      hwc_event_samples_t *sampled_data);
    /* Read current counter values and samples.  Read of samples is destructive.
       Note: hwcdrv_read_events is not supported on Linux.
       <overflow_data>: returns snapshot of counter values
       <sampled_data>: returns sampled data
       Return: 0 if successful
	  HWCFUNCS_ERROR_UNAVAIL if resource unavailable(e.g. called before initted)
	  (other values may be possible)
     */

    int (*hwcdrv_sighlr_restart)(const hwc_event_t* startVals);
    /* Restarts the counters at the given value.
	 This is intended to be called from SIGEMT handler;
       Input:
	 <startVals>: Solaris: new start values.
		      Linux: pointer may be NULL; startVals is ignored.
       Return: 0 if successful
	  or a cpc return code upon error.
     */

    int (*hwcdrv_lwp_suspend)(void);
    /* Attempt to stop counters on this lwp only.
	 hwcdrv_lwp_resume() should be used to restart counters.
       Return: 0 if successful
	  or a cpc return code upon error.
     */

    int (*hwcdrv_lwp_resume)(void);
    /* Attempt to restart counters on this lwp when counters were
	 stopped with hwcdrv_lwp_suspend().
       Return: 0 if successful
	  or a cpc return code upon error.
     */

    int (*hwcdrv_free_counters)(void);
    /* Stops counters on this lwp only and frees resources.
	 This will fail w/ unpredictable results if other lwps's are
	 still running.  After this call returns,
	 hwcdrv_create_counters() may be called with new values.
       Return: 0 if successful
	  or a cpc return code upon error.
     */

    int (*hwcdrv_lwp_init)(void);
    /* per-thread counter init.
	 Solaris: nop.
	 Linux: just after thread creation call this from inside thread
	      to create context and start counters.
       Return: 0 if successful
	  or a perfctr return code upon error
     */

    void (*hwcdrv_lwp_fini)(void);
    /* per-thread counter cleanup.
	 Solaris: nop.
	 Linux: call in each thread upon thread destruction.
     */

    int hwcdrv_init_status;
  } hwcdrv_api_t;

  extern hwcdrv_api_t *get_hwcdrv ();
  extern hwcdrv_api_t *__collector_get_hwcdrv ();
  extern int __collector_hwcfuncs_bind_descriptor (const char *defstring);
  extern Hwcentry **__collector_hwcfuncs_get_ctrs (unsigned *defcnt);
  extern hwcdrv_api_t *hwcdrv_drivers[]; // array of available drivers

  /* prototypes for internal use by hwcdrv drivers */
  typedef struct
  { // see hwcdrv_get_info() for field definitions
    int cpcN_cpuver;
    uint_t cpcN_npics;
    const char *cpcN_docref;
    const char *cpcN_cciname;
  } hwcdrv_about_t;

  extern int hwcdrv_assign_all_regnos (Hwcentry* entries[], unsigned numctrs);
  /* assign user's counters to specific CPU registers */

  extern int hwcdrv_lookup_cpuver (const char * cpcN_cciname);
  /* returns hwc_cpus.h ID for a given string. */

  extern void hwcfuncs_int_capture_errmsg (const char *fn, int subcode,
					   const char *fmt, va_list ap);
#define logerr  hwcfuncs_int_logerr

  /*---------------------------------------------------------------------------*/
  /* prototypes for internal use by linux hwcdrv drivers */
#define PERFCTR_FIXED_MAGIC 0x40000000 /* tells perfctr to use intel fixed pmcs */
#define PERFCTR_UMASK_SHIFT 8
#define EXTENDED_EVNUM_2_EVSEL(evnum) \
  ( (((eventsel_t)(evnum) & 0x0f00ULL) << 24) | ((eventsel_t)(evnum) & ~0x0f00ULL) )

  typedef uint64_t eventsel_t;
  extern int  hwcfuncs_get_x86_eventsel (unsigned int regno, const char *int_name,
			     eventsel_t *return_event, uint_t *return_pmc_sel);

  typedef int (hwcdrv_get_events_fn_t) (hwcf_hwc_cb_t *hwc_cb);
  typedef int (hwcdrv_get_eventnum_fn_t) (const char *eventname, uint_t pmc,
					  eventsel_t *eventnum,
					  eventsel_t *valid_umask, uint_t *pmc_sel);
  extern hwcdrv_get_eventnum_fn_t *hwcdrv_get_x86_eventnum;

  typedef struct
  {
    const char * attrname;  // user-visible name of attribute
    int is_inverted;        // nonzero means boolean attribute is inverted
    eventsel_t mask;        // which attribute bits can be set?
    eventsel_t shift;       // how far to shift bits for use in x86 register
  } attr_info_t;
  extern const attr_info_t *perfctr_attrs_table;

  /* hdrv_pcbe api: cpu-specific drivers for Linux */
  typedef struct
  {
    int (*hdrv_pcbe_init)(void);
    uint_t (*hdrv_pcbe_ncounters)(void);
    const char *(*hdrv_pcbe_impl_name)(void);
    const char *(*hdrv_pcbe_cpuref)(void);
    int (*hdrv_pcbe_get_events)(hwcf_hwc_cb_t *hwc_cb);
    int (*hdrv_pcbe_get_eventnum)(const char * eventname, uint_t pmc,
				  eventsel_t *eventnum, eventsel_t *valid_umask,
				  uint_t *pmc_sel);
  } hdrv_pcbe_api_t;

#ifdef __cplusplus
}
#endif

#endif
