/* ----------------------------------------------------------------------- *
 *
 *   Copyright 2006 Erwan Velu - All Rights Reserved
 *
 *   Permission is hereby granted, free of charge, to any person
 *   obtaining a copy of this software and associated documentation
 *   files (the "Software"), to deal in the Software without
 *   restriction, including without limitation the rights to use,
 *   copy, modify, merge, publish, distribute, sublicense, and/or
 *   sell copies of the Software, and to permit persons to whom
 *   the Software is furnished to do so, subject to the following
 *   conditions:
 *
 *   The above copyright notice and this permission notice shall
 *   be included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 *
 * ----------------------------------------------------------------------- */

#ifndef _CPUID_H
#define _CPUID_H

#include <stdbool.h>
#include <stdint.h>
#include <cpufeature.h>
#include <sys/cpu.h>
#include <klibc/compiler.h>

#define PAGE_SIZE 4096

#define CPU_MODEL_SIZE  48
#define CPU_VENDOR_SIZE 48

typedef struct {
	bool fpu; /* Onboard FPU */
	bool vme; /* Virtual Mode Extensions */
	bool de;  /* Debugging Extensions */
	bool pse; /* Page Size Extensions */
	bool tsc; /* Time Stamp Counter */
	bool msr; /* Model-Specific Registers, RDMSR, WRMSR */
	bool pae; /* Physical Address Extensions */
	bool mce; /* Machine Check Architecture */
	bool cx8; /* CMPXCHG8 instruction */
	bool apic;/* Onboard APIC */
	bool sep; /* SYSENTER/SYSEXIT */
	bool mtrr;/* Memory Type Range Registers */
	bool pge; /* Page Global Enable */
	bool mca; /* Machine Check Architecture */
	bool cmov;/* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
	bool pat; /* Page Attribute Table */
	bool pse_36; /* 36-bit PSEs */
	bool psn; /* Processor serial number */
	bool clflsh; /* Supports the CLFLUSH instruction */
	bool dts; /* Debug Trace Store */
	bool acpi;/* ACPI via MSR */
	bool mmx; /* Multimedia Extensions */
	bool fxsr;/* FXSAVE and FXRSTOR instructions (fast save and restore */
                  /* of FPU context), and CR4.OSFXSR available */
	bool sse; /* Streaming SIMD Extensions */
	bool sse2;/* Streaming SIMD Extensions 2*/
	bool ss;  /* CPU self snoop */
	bool htt; /* Hyper-Threading */
	bool acc; /* Automatic clock control */
	bool syscall; /* SYSCALL/SYSRET */
	bool mp;  /* MP Capable. */
	bool nx;  /* Execute Disable */
	bool mmxext;  /* AMD MMX extensions */
	bool lm;  /* Long Mode (x86-64) */
	bool nowext;/* AMD 3DNow! extensions */
	bool now;   /* 3DNow! */
	bool smp;  /* A smp configuration has been found*/
} s_cpu_flags;

typedef struct {
	char        vendor[CPU_VENDOR_SIZE];
	uint8_t     vendor_id;
	uint8_t     family;
	char        model[CPU_MODEL_SIZE];
	uint8_t     model_id;
	uint8_t     stepping;
	s_cpu_flags flags;
} s_cpu;

/**********************************************************************************/
/**********************************************************************************/
/* From this point this is some internal stuff mainly taken from the linux kernel */
/**********************************************************************************/
/**********************************************************************************/

/*
 * EFLAGS bits
 */
#define X86_EFLAGS_CF   0x00000001 /* Carry Flag */
#define X86_EFLAGS_PF   0x00000004 /* Parity Flag */
#define X86_EFLAGS_AF   0x00000010 /* Auxillary carry Flag */
#define X86_EFLAGS_ZF   0x00000040 /* Zero Flag */
#define X86_EFLAGS_SF   0x00000080 /* Sign Flag */
#define X86_EFLAGS_TF   0x00000100 /* Trap Flag */
#define X86_EFLAGS_IF   0x00000200 /* Interrupt Flag */
#define X86_EFLAGS_DF   0x00000400 /* Direction Flag */
#define X86_EFLAGS_OF   0x00000800 /* Overflow Flag */
#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */
#define X86_EFLAGS_NT   0x00004000 /* Nested Task */
#define X86_EFLAGS_RF   0x00010000 /* Resume Flag */
#define X86_EFLAGS_VM   0x00020000 /* Virtual Mode */
#define X86_EFLAGS_AC   0x00040000 /* Alignment Check */
#define X86_EFLAGS_VIF  0x00080000 /* Virtual Interrupt Flag */
#define X86_EFLAGS_VIP  0x00100000 /* Virtual Interrupt Pending */
#define X86_EFLAGS_ID   0x00200000 /* CPUID detection flag */

#define X86_VENDOR_INTEL 0
#define X86_VENDOR_CYRIX 1
#define X86_VENDOR_AMD 2
#define X86_VENDOR_UMC 3
#define X86_VENDOR_NEXGEN 4
#define X86_VENDOR_CENTAUR 5
#define X86_VENDOR_RISE 6
#define X86_VENDOR_TRANSMETA 7
#define X86_VENDOR_NSC 8
#define X86_VENDOR_NUM 9
#define X86_VENDOR_UNKNOWN 0xff

static inline __purefunc bool test_bit(int nr, const uint32_t *addr)
{
        return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
}

#define cpu_has(c, bit)                test_bit(bit, (c)->x86_capability)

/*
 *  CPU type and hardware bug flags. Kept separately for each CPU.
 *  Members of this structure are referenced in head.S, so think twice
 *  before touching them. [mj]
 */

struct cpuinfo_x86 {
        uint8_t  x86;            /* CPU family */
        uint8_t  x86_vendor;     /* CPU vendor */
        uint8_t  x86_model;
        uint8_t  x86_mask;
        char     wp_works_ok;    /* It doesn't on 386's */
        char     hlt_works_ok;   /* Problems on some 486Dx4's and old 386's */
        char     hard_math;
        char     rfu;
        int      cpuid_level;    /* Maximum supported CPUID level, -1=no CPUID */
        uint32_t x86_capability[NCAPINTS];
        char     x86_vendor_id[16];
        char     x86_model_id[64];
        int      x86_cache_size;	/* in KB, if available */
        int	 x86_cache_alignment;    /* in bytes */
        char     fdiv_bug;
        char     f00f_bug;
        char     coma_bug;
        char     pad0;
        int      x86_power;
        unsigned long loops_per_jiffy;
#ifdef CONFIG_SMP
        cpumask_t llc_shared_map;       /* cpus sharing the last level cache */
#endif
        unsigned char x86_max_cores;    /* cpuid returned max cores value */
        unsigned char booted_cores;     /* number of cores as seen by OS */
        unsigned char apicid;
} __attribute__((__packed__));
#endif

struct cpu_model_info {
        int vendor;
        int family;
        char *model_names[16];
};

/* attempt to consolidate cpu attributes */
struct cpu_dev {
        char    * c_vendor;

        /* some have two possibilities for cpuid string */
        char    * c_ident[2];

        struct          cpu_model_info c_models[4];

        void            (*c_init)(struct cpuinfo_x86 * c);
        void            (*c_identify)(struct cpuinfo_x86 * c);
        unsigned int    (*c_size_cache)(struct cpuinfo_x86 * c, unsigned int size);
};

/*
 * Structure definitions for SMP machines following the
 * Intel Multiprocessing Specification 1.1 and 1.4.
 */

/*
 * This tag identifies where the SMP configuration
 * information is.
 */

#define SMP_MAGIC_IDENT (('_'<<24)|('P'<<16)|('M'<<8)|'_')

struct intel_mp_floating
{
        char     mpf_signature[4];      /* "_MP_"                       */
        uint32_t mpf_physptr;		/* Configuration table address  */
        uint8_t  mpf_length;		/* Our length (paragraphs)      */
        uint8_t  mpf_specification;	/* Specification version        */
        uint8_t  mpf_checksum;		/* Checksum (makes sum 0)       */
        uint8_t  mpf_feature1;		/* Standard or configuration ?  */
        uint8_t  mpf_feature2;		/* Bit7 set for IMCR|PIC        */
        uint8_t  mpf_feature3;		/* Unused (0)                   */
        uint8_t  mpf_feature4;		/* Unused (0)                   */
        uint8_t  mpf_feature5;		/* Unused (0)                   */
};


extern void get_cpu_vendor(struct cpuinfo_x86 *c);
extern void detect_cpu(s_cpu *cpu);
