/* SPDX-License-Identifier: MIT */
/*
 * Structure definitions for HVM state that is held by Xen and must
 * be saved along with the domain's memory and device-model state.
 *
 * Copyright (c) 2007 XenSource Ltd.
 */

#ifndef __XEN_PUBLIC_HVM_SAVE_X86_H__
#define __XEN_PUBLIC_HVM_SAVE_X86_H__

#include "../../xen.h"

/*
 * Save/restore header: general info about the save file.
 */

#define HVM_FILE_MAGIC   0x54381286
#define HVM_FILE_VERSION 0x00000001

struct hvm_save_header {
    uint32_t magic;             /* Must be HVM_FILE_MAGIC */
    uint32_t version;           /* File format version */
    uint64_t changeset;         /* Version of Xen that saved this file */
    uint32_t cpuid;             /* CPUID[0x01][%eax] on the saving machine */
    uint32_t gtsc_khz;        /* Guest's TSC frequency in kHz */
};

DECLARE_HVM_SAVE_TYPE(HEADER, 1, struct hvm_save_header);


/*
 * Processor
 *
 * Compat:
 *     - Pre-3.4 didn't have msr_tsc_aux
 *     - Pre-4.7 didn't have fpu_initialised
 */

struct hvm_hw_cpu {
    uint8_t  fpu_regs[512];

    uint64_t rax;
    uint64_t rbx;
    uint64_t rcx;
    uint64_t rdx;
    uint64_t rbp;
    uint64_t rsi;
    uint64_t rdi;
    uint64_t rsp;
    uint64_t r8;
    uint64_t r9;
    uint64_t r10;
    uint64_t r11;
    uint64_t r12;
    uint64_t r13;
    uint64_t r14;
    uint64_t r15;

    uint64_t rip;
    uint64_t rflags;

    uint64_t cr0;
    uint64_t cr2;
    uint64_t cr3;
    uint64_t cr4;

    uint64_t dr0;
    uint64_t dr1;
    uint64_t dr2;
    uint64_t dr3;
    uint64_t dr6;
    uint64_t dr7;

    uint32_t cs_sel;
    uint32_t ds_sel;
    uint32_t es_sel;
    uint32_t fs_sel;
    uint32_t gs_sel;
    uint32_t ss_sel;
    uint32_t tr_sel;
    uint32_t ldtr_sel;

    uint32_t cs_limit;
    uint32_t ds_limit;
    uint32_t es_limit;
    uint32_t fs_limit;
    uint32_t gs_limit;
    uint32_t ss_limit;
    uint32_t tr_limit;
    uint32_t ldtr_limit;
    uint32_t idtr_limit;
    uint32_t gdtr_limit;

    uint64_t cs_base;
    uint64_t ds_base;
    uint64_t es_base;
    uint64_t fs_base;
    uint64_t gs_base;
    uint64_t ss_base;
    uint64_t tr_base;
    uint64_t ldtr_base;
    uint64_t idtr_base;
    uint64_t gdtr_base;

    uint32_t cs_arbytes;
    uint32_t ds_arbytes;
    uint32_t es_arbytes;
    uint32_t fs_arbytes;
    uint32_t gs_arbytes;
    uint32_t ss_arbytes;
    uint32_t tr_arbytes;
    uint32_t ldtr_arbytes;

    uint64_t sysenter_cs;
    uint64_t sysenter_esp;
    uint64_t sysenter_eip;

    /* msr for em64t */
    uint64_t shadow_gs;

    /* msr content saved/restored. */
    uint64_t msr_flags; /* Obsolete, ignored. */
    uint64_t msr_lstar;
    uint64_t msr_star;
    uint64_t msr_cstar;
    uint64_t msr_syscall_mask;
    uint64_t msr_efer;
    uint64_t msr_tsc_aux;

    /* guest's idea of what rdtsc() would return */
    uint64_t tsc;

    /* pending event, if any */
    union {
        uint32_t pending_event;
        struct {
            uint8_t  pending_vector:8;
            uint8_t  pending_type:3;
            uint8_t  pending_error_valid:1;
            uint32_t pending_reserved:19;
            uint8_t  pending_valid:1;
        };
    };
    /* error code for pending event */
    uint32_t error_code;

#define _XEN_X86_FPU_INITIALISED        0
#define XEN_X86_FPU_INITIALISED         (1U<<_XEN_X86_FPU_INITIALISED)
    uint32_t flags;
    uint32_t pad0;
};

struct hvm_hw_cpu_compat {
    uint8_t  fpu_regs[512];

    uint64_t rax;
    uint64_t rbx;
    uint64_t rcx;
    uint64_t rdx;
    uint64_t rbp;
    uint64_t rsi;
    uint64_t rdi;
    uint64_t rsp;
    uint64_t r8;
    uint64_t r9;
    uint64_t r10;
    uint64_t r11;
    uint64_t r12;
    uint64_t r13;
    uint64_t r14;
    uint64_t r15;

    uint64_t rip;
    uint64_t rflags;

    uint64_t cr0;
    uint64_t cr2;
    uint64_t cr3;
    uint64_t cr4;

    uint64_t dr0;
    uint64_t dr1;
    uint64_t dr2;
    uint64_t dr3;
    uint64_t dr6;
    uint64_t dr7;

    uint32_t cs_sel;
    uint32_t ds_sel;
    uint32_t es_sel;
    uint32_t fs_sel;
    uint32_t gs_sel;
    uint32_t ss_sel;
    uint32_t tr_sel;
    uint32_t ldtr_sel;

    uint32_t cs_limit;
    uint32_t ds_limit;
    uint32_t es_limit;
    uint32_t fs_limit;
    uint32_t gs_limit;
    uint32_t ss_limit;
    uint32_t tr_limit;
    uint32_t ldtr_limit;
    uint32_t idtr_limit;
    uint32_t gdtr_limit;

    uint64_t cs_base;
    uint64_t ds_base;
    uint64_t es_base;
    uint64_t fs_base;
    uint64_t gs_base;
    uint64_t ss_base;
    uint64_t tr_base;
    uint64_t ldtr_base;
    uint64_t idtr_base;
    uint64_t gdtr_base;

    uint32_t cs_arbytes;
    uint32_t ds_arbytes;
    uint32_t es_arbytes;
    uint32_t fs_arbytes;
    uint32_t gs_arbytes;
    uint32_t ss_arbytes;
    uint32_t tr_arbytes;
    uint32_t ldtr_arbytes;

    uint64_t sysenter_cs;
    uint64_t sysenter_esp;
    uint64_t sysenter_eip;

    /* msr for em64t */
    uint64_t shadow_gs;

    /* msr content saved/restored. */
    uint64_t msr_flags; /* Obsolete, ignored. */
    uint64_t msr_lstar;
    uint64_t msr_star;
    uint64_t msr_cstar;
    uint64_t msr_syscall_mask;
    uint64_t msr_efer;
    /*uint64_t msr_tsc_aux; COMPAT */

    /* guest's idea of what rdtsc() would return */
    uint64_t tsc;

    /* pending event, if any */
    union {
        uint32_t pending_event;
        struct {
            uint8_t  pending_vector:8;
            uint8_t  pending_type:3;
            uint8_t  pending_error_valid:1;
            uint32_t pending_reserved:19;
            uint8_t  pending_valid:1;
        };
    };
    /* error code for pending event */
    uint32_t error_code;
};

static inline int _hvm_hw_fix_cpu(void *h, uint32_t size) {

    union hvm_hw_cpu_union {
        struct hvm_hw_cpu nat;
        struct hvm_hw_cpu_compat cmp;
    } *ucpu = (union hvm_hw_cpu_union *)h;

    if ( size == sizeof(struct hvm_hw_cpu_compat) )
    {
        /*
         * If we copy from the end backwards, we should
         * be able to do the modification in-place.
         */
        ucpu->nat.error_code = ucpu->cmp.error_code;
        ucpu->nat.pending_event = ucpu->cmp.pending_event;
        ucpu->nat.tsc = ucpu->cmp.tsc;
        ucpu->nat.msr_tsc_aux = 0;
    }
    /* Mimic the old behaviour by unconditionally setting fpu_initialised. */
    ucpu->nat.flags = XEN_X86_FPU_INITIALISED;

    return 0;
}

DECLARE_HVM_SAVE_TYPE_COMPAT(CPU, 2, struct hvm_hw_cpu, \
                             struct hvm_hw_cpu_compat, _hvm_hw_fix_cpu);

/*
 * PIC
 */

struct hvm_hw_vpic {
    /* IR line bitmasks. */
    uint8_t irr;
    uint8_t imr;
    uint8_t isr;

    /* Line IRx maps to IRQ irq_base+x */
    uint8_t irq_base;

    /*
     * Where are we in ICW2-4 initialisation (0 means no init in progress)?
     * Bits 0-1 (=x): Next write at A=1 sets ICW(x+1).
     * Bit 2: ICW1.IC4  (1 == ICW4 included in init sequence)
     * Bit 3: ICW1.SNGL (0 == ICW3 included in init sequence)
     */
    uint8_t init_state:4;

    /* IR line with highest priority. */
    uint8_t priority_add:4;

    /* Reads from A=0 obtain ISR or IRR? */
    uint8_t readsel_isr:1;

    /* Reads perform a polling read? */
    uint8_t poll:1;

    /* Automatically clear IRQs from the ISR during INTA? */
    uint8_t auto_eoi:1;

    /* Automatically rotate IRQ priorities during AEOI? */
    uint8_t rotate_on_auto_eoi:1;

    /* Exclude slave inputs when considering in-service IRQs? */
    uint8_t special_fully_nested_mode:1;

    /* Special mask mode excludes masked IRs from AEOI and priority checks. */
    uint8_t special_mask_mode:1;

    /* Is this a master PIC or slave PIC? (NB. This is not programmable.) */
    uint8_t is_master:1;

    /* Edge/trigger selection. */
    uint8_t elcr;

    /* Virtual INT output. */
    uint8_t int_output;
};

DECLARE_HVM_SAVE_TYPE(PIC, 3, struct hvm_hw_vpic);


/*
 * IO-APIC
 */

union vioapic_redir_entry
{
    uint64_t bits;
    struct {
        uint8_t vector;
        uint8_t delivery_mode:3;
        uint8_t dest_mode:1;
        uint8_t delivery_status:1;
        uint8_t polarity:1;
        uint8_t remote_irr:1;
        uint8_t trig_mode:1;
        uint8_t mask:1;
        uint8_t reserve:7;
        uint8_t reserved[4];
        uint8_t dest_id;
    } fields;
};

#define VIOAPIC_NUM_PINS  48 /* 16 ISA IRQs, 32 non-legacy PCI IRQS. */

#define XEN_HVM_VIOAPIC(name, cnt)                      \
    struct name {                                       \
        uint64_t base_address;                          \
        uint32_t ioregsel;                              \
        uint32_t id;                                    \
        union vioapic_redir_entry redirtbl[cnt];        \
    }

XEN_HVM_VIOAPIC(hvm_hw_vioapic, VIOAPIC_NUM_PINS);

#ifndef __XEN__
#undef XEN_HVM_VIOAPIC
#else
#undef VIOAPIC_NUM_PINS
#endif

DECLARE_HVM_SAVE_TYPE(IOAPIC, 4, struct hvm_hw_vioapic);


/*
 * LAPIC
 */

struct hvm_hw_lapic {
    uint64_t             apic_base_msr;
    uint32_t             disabled; /* VLAPIC_xx_DISABLED */
    uint32_t             timer_divisor;
    uint64_t             tdt_msr;
    uint32_t             pending_esr;
};

DECLARE_HVM_SAVE_TYPE(LAPIC, 5, struct hvm_hw_lapic);

struct hvm_hw_lapic_regs {
    uint8_t data[1024];
};

DECLARE_HVM_SAVE_TYPE(LAPIC_REGS, 6, struct hvm_hw_lapic_regs);


/*
 * IRQs
 */

struct hvm_hw_pci_irqs {
    /*
     * Virtual interrupt wires for a single PCI bus.
     * Indexed by: device*4 + INTx#.
     */
    union {
        unsigned long i[16 / sizeof (unsigned long)]; /* DECLARE_BITMAP(i, 32*4); */
        uint64_t pad[2];
    };
};

DECLARE_HVM_SAVE_TYPE(PCI_IRQ, 7, struct hvm_hw_pci_irqs);

struct hvm_hw_isa_irqs {
    /*
     * Virtual interrupt wires for ISA devices.
     * Indexed by ISA IRQ (assumes no ISA-device IRQ sharing).
     */
    union {
        unsigned long i[1];  /* DECLARE_BITMAP(i, 16); */
        uint64_t pad[1];
    };
};

DECLARE_HVM_SAVE_TYPE(ISA_IRQ, 8, struct hvm_hw_isa_irqs);

struct hvm_hw_pci_link {
    /*
     * PCI-ISA interrupt router.
     * Each PCI <device:INTx#> is 'wire-ORed' into one of four links using
     * the traditional 'barber's pole' mapping ((device + INTx#) & 3).
     * The router provides a programmable mapping from each link to a GSI.
     */
    uint8_t route[4];
    uint8_t pad0[4];
};

DECLARE_HVM_SAVE_TYPE(PCI_LINK, 9, struct hvm_hw_pci_link);

/*
 *  PIT
 */

struct hvm_hw_pit {
    struct hvm_hw_pit_channel {
        uint32_t count; /* can be 65536 */
        uint16_t latched_count;
        uint8_t count_latched;
        uint8_t status_latched;
        uint8_t status;
        uint8_t read_state;
        uint8_t write_state;
        uint8_t write_latch;
        uint8_t rw_mode;
        uint8_t mode;
        uint8_t bcd; /* not supported */
        uint8_t gate; /* timer start */
    } channels[3];  /* 3 x 16 bytes */
    uint32_t speaker_data_on;
    uint32_t pad0;
};

DECLARE_HVM_SAVE_TYPE(PIT, 10, struct hvm_hw_pit);


/*
 * RTC
 */

#define RTC_CMOS_SIZE 14
struct hvm_hw_rtc {
    /* CMOS bytes */
    uint8_t cmos_data[RTC_CMOS_SIZE];
    /* Index register for 2-part operations */
    uint8_t cmos_index;
    uint8_t pad0;
    /* RTC offset from host time */
    int64_t rtc_offset;
};

DECLARE_HVM_SAVE_TYPE(RTC, 11, struct hvm_hw_rtc);


/*
 * HPET
 */

#define HPET_TIMER_NUM     3    /* 3 timers supported now */
struct hvm_hw_hpet {
    /* Memory-mapped, software visible registers */
    uint64_t capability;        /* capabilities */
    uint64_t res0;              /* reserved */
    uint64_t config;            /* configuration */
    uint64_t res1;              /* reserved */
    uint64_t isr;               /* interrupt status reg */
    uint64_t res2[25];          /* reserved */
    uint64_t mc64;              /* main counter */
    uint64_t res3;              /* reserved */
    struct {                    /* timers */
        uint64_t config;        /* configuration/cap */
        uint64_t cmp;           /* comparator */
        uint64_t fsb;           /* FSB route, not supported now */
        uint64_t res4;          /* reserved */
    } timers[HPET_TIMER_NUM];
    uint64_t res5[4*(24-HPET_TIMER_NUM)];  /* reserved, up to 0x3ff */

    /* Hidden register state */
    uint64_t period[HPET_TIMER_NUM]; /* Last value written to comparator */
};

DECLARE_HVM_SAVE_TYPE(HPET, 12, struct hvm_hw_hpet);


/*
 * PM timer
 */

struct hvm_hw_pmtimer {
    uint32_t tmr_val;   /* PM_TMR_BLK.TMR_VAL: 32bit free-running counter */
    uint16_t pm1a_sts;  /* PM1a_EVT_BLK.PM1a_STS: status register */
    uint16_t pm1a_en;   /* PM1a_EVT_BLK.PM1a_EN: enable register */
};

DECLARE_HVM_SAVE_TYPE(PMTIMER, 13, struct hvm_hw_pmtimer);

/*
 * MTRR MSRs
 */

struct hvm_hw_mtrr {
#define MTRR_VCNT 8
#define NUM_FIXED_MSR 11
    uint64_t msr_pat_cr;
    /* mtrr physbase & physmask msr pair*/
    uint64_t msr_mtrr_var[MTRR_VCNT*2];
    uint64_t msr_mtrr_fixed[NUM_FIXED_MSR];
    uint64_t msr_mtrr_cap;
    uint64_t msr_mtrr_def_type;
};

DECLARE_HVM_SAVE_TYPE(MTRR, 14, struct hvm_hw_mtrr);

/*
 * The save area of XSAVE/XRSTOR.
 */

struct hvm_hw_cpu_xsave {
    uint64_t xfeature_mask;        /* Ignored */
    uint64_t xcr0;                 /* Updated by XSETBV */
    uint64_t xcr0_accum;           /* Updated by XSETBV */
    struct {
        struct { char x[512]; } fpu_sse;

        struct hvm_hw_cpu_xsave_hdr {
            uint64_t xstate_bv;         /* Updated by XRSTOR */
            uint64_t xcomp_bv;          /* Updated by XRSTOR{C,S} */
            uint64_t reserved[6];
        } xsave_hdr;                    /* The 64-byte header */
    } save_area;
};

#define CPU_XSAVE_CODE  16

/*
 * Viridian hypervisor context.
 */

struct hvm_viridian_domain_context {
    uint64_t hypercall_gpa;
    uint64_t guest_os_id;
    uint64_t time_ref_count;
    uint64_t reference_tsc;
};

DECLARE_HVM_SAVE_TYPE(VIRIDIAN_DOMAIN, 15, struct hvm_viridian_domain_context);

struct hvm_viridian_vcpu_context {
    uint64_t vp_assist_msr;
    uint8_t  apic_assist_pending;
    uint8_t  _pad[7];
    uint64_t simp_msr;
    uint64_t sint_msr[16];
    uint64_t stimer_config_msr[4];
    uint64_t stimer_count_msr[4];
};

DECLARE_HVM_SAVE_TYPE(VIRIDIAN_VCPU, 17, struct hvm_viridian_vcpu_context);

struct hvm_vmce_vcpu {
    uint64_t caps;
    uint64_t mci_ctl2_bank0;
    uint64_t mci_ctl2_bank1;
    uint64_t mcg_ext_ctl;
};

DECLARE_HVM_SAVE_TYPE(VMCE_VCPU, 18, struct hvm_vmce_vcpu);

struct hvm_tsc_adjust {
    uint64_t tsc_adjust;
};

DECLARE_HVM_SAVE_TYPE(TSC_ADJUST, 19, struct hvm_tsc_adjust);


struct hvm_msr {
    uint32_t count;
    struct hvm_one_msr {
        uint32_t index;
        uint32_t _rsvd;
        uint64_t val;
    } msr[XEN_FLEX_ARRAY_DIM];
};

#define CPU_MSR_CODE  20

/* Range 22 - 34 (inclusive) reserved for Amazon */

/*
 * Largest type-code in use
 */
#define HVM_SAVE_CODE_MAX 20

#endif /* __XEN_PUBLIC_HVM_SAVE_X86_H__ */

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */
