//
//  cpu_topology.h
//  BetaOS
//
//  Created by Adam Kopeć on 7/12/16.
//  Copyright © 2016 Adam Kopeć. All rights reserved.
//

#ifndef cpu_topology_h
#define cpu_topology_h

#include <stdint.h>
#include <stdbool.h>

struct x86_die;
struct x86_core;
struct x86_pkg;
struct x86_lcpu;

/*
 * Cache structure that can be used to identify the cache heirarchy.
 */
typedef struct x86_cpu_cache {
    struct x86_cpu_cache *next;		/* next cache at this level/lcpu */
    struct x86_die      *die;		/* die containing this cache (only for LLC) */
    uint8_t             maxcpus;	/* maximum # of cpus that can share */
    uint8_t             nlcpus;		/* # of logical cpus sharing this cache */
    uint8_t             type;		/* type of cache */
    uint8_t             level;		/* level of cache */
    uint16_t            ways;		/* # of ways in cache */
    uint16_t            partitions;	/* # of partitions in cache */
    uint16_t            line_size;	/* size of a cache line */
    uint32_t            cache_size;	/* total size of cache */
    struct x86_lcpu     *cpus[0];	/* cpus sharing this cache */
} x86_cpu_cache_t;

#define CPU_CACHE_TYPE_DATA	1	/* data cache */
#define CPU_CACHE_TYPE_INST	2	/* instruction cache */
#define CPU_CACHE_TYPE_UNIF	3	/* unified cache */

#define CPU_CACHE_DEPTH_L1	0
#define CPU_CACHE_DEPTH_L2	1
#define CPU_CACHE_DEPTH_L3	2

#define MAX_CACHE_DEPTH		3	/* deepest cache */

struct pmc;
struct cpu_data;
struct mca_state;

/*
 * Define the states that a (logical) CPU can be in.
 *
 * LCPU_OFF	This indicates that the CPU is "off".  It requires a full
 *		restart.  This is the state of a CPU when the system first
 *		boots or when it comes out of "sleep" (aka S3/S5).
 *
 * LCPU_HALT This indicates that the CPU has been "halted".  It has been
 *		removed from the system but still retains its internal state
 *		so that it can be quickly brought back on-line.
 *
 * LCPU_NONSCHED This indicates that the CPU is not schedulable.  It
 *		will still appear in the system as a viable CPU however no
 *		work will be sceduled on it.
 *
 * LCPU_PAUSE This indicates that the CPU is "paused".  This is usually
 *		done only during kernel debug.
 *
 * LCPU_IDLE This indicates that the CPU is idle.  The scheduler has
 *		determined that there is no work for this CPU to do.
 *
 * LCPU_RUN	This indicates that the CPU is running code and performing work.
 *
 * In normal system operation, CPUs will usually be transitioning between
 * LCPU_IDLE and LCPU_RUN.
 */
typedef enum lcpu_state {
    LCPU_OFF		= 0,	/* 0 so the right thing happens on boot */
    LCPU_HALT		= 1,
    LCPU_NONSCHED	= 2,
    LCPU_PAUSE		= 3,
    LCPU_IDLE		= 4,
    LCPU_RUN		= 5,
} lcpu_state_t;

/*
 * In each topology structure there are two numbers: a logical number and a
 * physical number.
 *
 * The logical numbers represent the ID of that structure
 * relative to the enclosing structure and always starts at 0.  So when using
 * logical numbers, it is necessary to specify all elements in the topology
 * (ie to "name" a logical CPU using logical numbers, 4 numbers are required:
 * package, die, core, logical CPU).
 *
 * The physical numbers represent the ID of that structure and is unique (for
 * that structure) across the entire topology.
 *
 * The logical CPU structure contains a third number which is the CPU number.
 * This number is identical to the CPU number used in other parts of the kernel.
 */
typedef struct x86_lcpu {
    struct x86_lcpu         *next_in_core;	/* next logical cpu in core */
    struct x86_lcpu         *next_in_die;	/* next logical cpu in die */
    struct x86_lcpu         *next_in_pkg;	/* next logical cpu in package */
    struct x86_lcpu         *lcpu;          /* pointer back to self */
    struct x86_core         *core;          /* core containing the logical cpu */
    struct x86_die          *die;           /* die containing the logical cpu */
    struct x86_pkg          *package;       /* package containing the logical cpu */
    struct cpu_data         *cpu;           /* cpu_data structure */
    uint32_t                flags;
    uint32_t                cpu_num;        /* cpu number */
    uint32_t                lnum;           /* logical cpu number (within core) */
    uint32_t                pnum;           /* physical cpu number */
    bool                    master;         /* logical cpu is the master (boot) CPU */
    bool                    primary;        /* logical cpu is primary CPU in package */
    volatile lcpu_state_t	state;          /* state of the logical CPU */
    volatile bool           stopped;        /* used to indicate that the CPU has "stopped" */
    uint64_t                rtcPop;         /* next timer pop programmed */
    uint64_t                rtcDeadline;	/* next etimer-requested deadline */
    x86_cpu_cache_t         *caches[MAX_CACHE_DEPTH];
    void                    *pmStats;       /* Power management stats for lcpu */
    void                    *pmState;       /* Power management state for lcpu */
} x86_lcpu_t;

#define X86CORE_FL_PRESENT	0x80000000	/* core is present */
#define X86CORE_FL_READY	0x40000000	/* core struct is init'd */
#define X86CORE_FL_HAS_HPET	0x10000000	/* core has HPET assigned */
#define X86CORE_FL_HALTED	0x00008000	/* core is halted */
#define X86CORE_FL_IDLE		0x00004000	/* core is idle */

typedef struct x86_core {
    struct x86_core	*next_in_die;	/* next core in die */
    struct x86_core	*next_in_pkg;	/* next core in package */
    struct x86_die	*die;           /* die containing the core */
    struct x86_pkg	*package;       /* package containing core */
    struct x86_lcpu	*lcpus;         /* list of logical cpus in core */
    uint32_t		flags;
    uint32_t		lcore_num;      /* logical core # (unique within die) */
    uint32_t		pcore_num;      /* physical core # (globally unique) */
    uint32_t		num_lcpus;      /* Number of logical cpus */
    uint32_t		active_lcpus;	/* Number of {running, idle} cpus */
    void            *pmStats;       /* Power management stats for core */
    void            *pmState;       /* Power management state for core */
} x86_core_t;

#define X86DIE_FL_PRESENT	0x80000000	/* die is present */
#define X86DIE_FL_READY		0x40000000	/* die struct is init'd */

typedef struct x86_die {
    struct x86_die	*next_in_pkg;/* next die in package */
    struct x86_lcpu	*lcpus;		 /* list of lcpus in die */
    struct x86_core	*cores;		 /* list of cores in die */
    struct x86_pkg	*package;	 /* package containing the die */
    uint32_t		flags;
    uint32_t		ldie_num;	 /* logical die # (unique to package) */
    uint32_t		pdie_num;	 /* physical die # (globally unique) */
    uint32_t		num_cores;	 /* Number of cores in die */
    x86_cpu_cache_t	*LLC;		 /* LLC contained in this die */
    void            *pmStats;	 /* Power Management stats for die */
    void            *pmState;	 /* Power Management state for die */
} x86_die_t;

#define X86PKG_FL_PRESENT	0x80000000	/* package is present */
#define X86PKG_FL_READY		0x40000000	/* package struct init'd */
#define X86PKG_FL_HAS_HPET	0x10000000	/* package has HPET assigned */
#define X86PKG_FL_HALTED	0x00008000	/* package is halted */
#define X86PKG_FL_IDLE		0x00004000	/* package is idle */

typedef struct x86_pkg {
    struct x86_pkg      *next;		/* next package */
    struct x86_lcpu     *lcpus;		/* list of logical cpus in package */
    struct x86_core     *cores;		/* list of cores in package */
    struct x86_die      *dies;		/* list of dies in package */
    uint32_t            flags;
    uint32_t            lpkg_num;	/* logical package # */
    uint32_t            ppkg_num;	/* physical package # */
    uint32_t            num_dies;	/* number of dies in package */
    void                *pmStats;	/* Power Management stats for package*/
    void                *pmState;	/* Power Management state for package*/
    struct mca_state    *mca_state;	/* MCA state for memory errors */
    uint64_t            package_idle_exits;
    uint32_t            num_idle;
} x86_pkg_t;

extern x86_pkg_t	*x86_pkgs;	/* root of all CPU packages */

typedef struct x86_topology_parameters {
    uint32_t		LLCDepth;
    uint32_t		nCoresSharingLLC;
    uint32_t		nLCPUsSharingLLC;
    uint32_t		maxSharingLLC;
    uint32_t		nLThreadsPerCore;
    uint32_t		nPThreadsPerCore;
    uint32_t		nLCoresPerDie;
    uint32_t		nPCoresPerDie;
    uint32_t		nLDiesPerPackage;
    uint32_t		nPDiesPerPackage;
    uint32_t		nLThreadsPerDie;
    uint32_t		nPThreadsPerDie;
    uint32_t		nLThreadsPerPackage;
    uint32_t		nPThreadsPerPackage;
    uint32_t		nLCoresPerPackage;
    uint32_t		nPCoresPerPackage;
    uint32_t		nPackages;
    bool            stable;
} x86_topology_parameters_t;

#endif /* cpu_topology_h */
