#ifndef __HYPERV_H
#define __HYPERV_H

#include "libcflat.h"
#include "processor.h"

#define HYPERV_CPUID_FEATURES                   0x40000003

#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE     (1 << 1)
#define HV_X64_MSR_SYNIC_AVAILABLE              (1 << 2)
#define HV_X64_MSR_SYNTIMER_AVAILABLE           (1 << 3)

#define HV_X64_MSR_GUEST_OS_ID                  0x40000000
#define HV_X64_MSR_HYPERCALL                    0x40000001

#define HV_X64_MSR_TIME_REF_COUNT               0x40000020
#define HV_X64_MSR_REFERENCE_TSC                0x40000021

/* Define synthetic interrupt controller model specific registers. */
#define HV_X64_MSR_SCONTROL                     0x40000080
#define HV_X64_MSR_SVERSION                     0x40000081
#define HV_X64_MSR_SIEFP                        0x40000082
#define HV_X64_MSR_SIMP                         0x40000083
#define HV_X64_MSR_EOM                          0x40000084
#define HV_X64_MSR_SINT0                        0x40000090
#define HV_X64_MSR_SINT1                        0x40000091
#define HV_X64_MSR_SINT2                        0x40000092
#define HV_X64_MSR_SINT3                        0x40000093
#define HV_X64_MSR_SINT4                        0x40000094
#define HV_X64_MSR_SINT5                        0x40000095
#define HV_X64_MSR_SINT6                        0x40000096
#define HV_X64_MSR_SINT7                        0x40000097
#define HV_X64_MSR_SINT8                        0x40000098
#define HV_X64_MSR_SINT9                        0x40000099
#define HV_X64_MSR_SINT10                       0x4000009A
#define HV_X64_MSR_SINT11                       0x4000009B
#define HV_X64_MSR_SINT12                       0x4000009C
#define HV_X64_MSR_SINT13                       0x4000009D
#define HV_X64_MSR_SINT14                       0x4000009E
#define HV_X64_MSR_SINT15                       0x4000009F

/*
 * Synthetic Timer MSRs. Four timers per vcpu.
 */

#define HV_X64_MSR_STIMER0_CONFIG               0x400000B0
#define HV_X64_MSR_STIMER0_COUNT                0x400000B1
#define HV_X64_MSR_STIMER1_CONFIG               0x400000B2
#define HV_X64_MSR_STIMER1_COUNT                0x400000B3
#define HV_X64_MSR_STIMER2_CONFIG               0x400000B4
#define HV_X64_MSR_STIMER2_COUNT                0x400000B5
#define HV_X64_MSR_STIMER3_CONFIG               0x400000B6
#define HV_X64_MSR_STIMER3_COUNT                0x400000B7

#define HV_SYNIC_CONTROL_ENABLE                 (1ULL << 0)
#define HV_SYNIC_SIMP_ENABLE                    (1ULL << 0)
#define HV_SYNIC_SIEFP_ENABLE                   (1ULL << 0)
#define HV_SYNIC_SINT_MASKED                    (1ULL << 16)
#define HV_SYNIC_SINT_AUTO_EOI                  (1ULL << 17)
#define HV_SYNIC_SINT_VECTOR_MASK               (0xFF)
#define HV_SYNIC_SINT_COUNT                     16

#define HV_STIMER_ENABLE                (1ULL << 0)
#define HV_STIMER_PERIODIC              (1ULL << 1)
#define HV_STIMER_LAZY                  (1ULL << 2)
#define HV_STIMER_AUTOENABLE            (1ULL << 3)
#define HV_STIMER_SINT(config)          (__u8)(((config) >> 16) & 0x0F)

#define HV_SYNIC_STIMER_COUNT           (4)

/* Define synthetic interrupt controller message constants. */
#define HV_MESSAGE_SIZE                 (256)
#define HV_MESSAGE_PAYLOAD_BYTE_COUNT   (240)
#define HV_MESSAGE_PAYLOAD_QWORD_COUNT  (30)

/* Define hypervisor message types. */
enum hv_message_type {
        HVMSG_NONE                      = 0x00000000,

        /* Memory access messages. */
        HVMSG_UNMAPPED_GPA              = 0x80000000,
        HVMSG_GPA_INTERCEPT             = 0x80000001,

        /* Timer notification messages. */
        HVMSG_TIMER_EXPIRED                     = 0x80000010,

        /* Error messages. */
        HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020,
        HVMSG_UNRECOVERABLE_EXCEPTION   = 0x80000021,
        HVMSG_UNSUPPORTED_FEATURE               = 0x80000022,

        /* Trace buffer complete messages. */
        HVMSG_EVENTLOG_BUFFERCOMPLETE   = 0x80000040,

        /* Platform-specific processor intercept messages. */
        HVMSG_X64_IOPORT_INTERCEPT              = 0x80010000,
        HVMSG_X64_MSR_INTERCEPT         = 0x80010001,
        HVMSG_X64_CPUID_INTERCEPT               = 0x80010002,
        HVMSG_X64_EXCEPTION_INTERCEPT   = 0x80010003,
        HVMSG_X64_APIC_EOI                      = 0x80010004,
        HVMSG_X64_LEGACY_FP_ERROR               = 0x80010005
};

/* Define synthetic interrupt controller message flags. */
union hv_message_flags {
        uint8_t asu8;
        struct {
                uint8_t msg_pending:1;
                uint8_t reserved:7;
        };
};

union hv_port_id {
        uint32_t asu32;
        struct {
                uint32_t id:24;
                uint32_t reserved:8;
        } u;
};

/* Define port type. */
enum hv_port_type {
        HVPORT_MSG      = 1,
        HVPORT_EVENT            = 2,
        HVPORT_MONITOR  = 3
};

/* Define synthetic interrupt controller message header. */
struct hv_message_header {
        uint32_t message_type;
        uint8_t payload_size;
        union hv_message_flags message_flags;
        uint8_t reserved[2];
        union {
                uint64_t sender;
                union hv_port_id port;
        };
};

/* Define timer message payload structure. */
struct hv_timer_message_payload {
        uint32_t timer_index;
        uint32_t reserved;
        uint64_t expiration_time;       /* When the timer expired */
        uint64_t delivery_time; /* When the message was delivered */
};

/* Define synthetic interrupt controller message format. */
struct hv_message {
        struct hv_message_header header;
        union {
                uint64_t payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
        } u;
};

/* Define the synthetic interrupt message page layout. */
struct hv_message_page {
        struct hv_message sint_message[HV_SYNIC_SINT_COUNT];
};

#define HV_EVENT_FLAGS_COUNT	(256 * 8)

struct hv_event_flags {
	ulong flags[HV_EVENT_FLAGS_COUNT / (8 * sizeof(ulong))];
};

struct hv_event_flags_page {
	struct hv_event_flags slot[HV_SYNIC_SINT_COUNT];
};

#define HV_X64_MSR_HYPERCALL_ENABLE             0x1

#define HV_HYPERCALL_FAST               (1u << 16)

#define HVCALL_POST_MESSAGE                     0x5c
#define HVCALL_SIGNAL_EVENT                     0x5d

struct hv_input_post_message {
	u32 connectionid;
	u32 reserved;
	u32 message_type;
	u32 payload_size;
	u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
};

static inline bool synic_supported(void)
{
   return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
}

static inline bool stimer_supported(void)
{
    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
}

static inline bool hv_time_ref_counter_supported(void)
{
    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
}

void synic_sint_create(u8 sint, u8 vec, bool auto_eoi);
void synic_sint_set(u8 vcpu, u8 sint);
void synic_sint_destroy(u8 sint);
void msg_conn_create(u8 sint, u8 vec, u8 conn_id);
void msg_conn_destroy(u8 sint, u8 conn_id);
void evt_conn_create(u8 sint, u8 vec, u8 conn_id);
void evt_conn_destroy(u8 sint, u8 conn_id);

struct hv_reference_tsc_page {
        uint32_t tsc_sequence;
        uint32_t res1;
        uint64_t tsc_scale;
        int64_t tsc_offset;
};


#endif
