/*	$OpenBSD: vmmvar.h,v 1.15 2016/06/10 16:37:16 stefan Exp $	*/
/*
 * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * CPU capabilities for VMM operation
 */
#ifndef _MACHINE_VMMVAR_H_
#define _MACHINE_VMMVAR_H_

#define VMM_HV_SIGNATURE 	"OpenBSDVMM58"

#define VMM_MAX_MEM_RANGES	16
#define VMM_MAX_DISKS_PER_VM	2
#define VMM_MAX_PATH_DISK	128
#define VMM_MAX_NAME_LEN	32
#define VMM_MAX_KERNEL_PATH	128
#define VMM_MAX_VCPUS_PER_VM	64
#define VMM_MAX_VM_MEM_SIZE	(512 * 1024)
#define VMM_MAX_NICS_PER_VM	2

#define VMM_PCI_MMIO_BAR_BASE	0xF0000000
#define VMM_PCI_MMIO_BAR_END	0xF0FFFFFF
#define VMM_PCI_MMIO_BAR_SIZE	0x00010000
#define VMM_PCI_IO_BAR_BASE	0x1000
#define VMM_PCI_IO_BAR_END	0xFFFF
#define VMM_PCI_IO_BAR_SIZE	0x1000

/* VMX: Basic Exit Reasons */
#define VMX_EXIT_NMI				0
#define VMX_EXIT_EXTINT				1
#define VMX_EXIT_TRIPLE_FAULT			2
#define VMX_EXIT_INIT				3
#define VMX_EXIT_SIPI				4
#define VMX_EXIT_IO_SMI				5
#define VMX_EXIT_OTHER_SMI			6
#define VMX_EXIT_INT_WINDOW			7
#define VMX_EXIT_NMI_WINDOW			8
#define VMX_EXIT_TASK_SWITCH			9
#define VMX_EXIT_CPUID				10
#define VMX_EXIT_GETSEC				11
#define VMX_EXIT_HLT				12
#define VMX_EXIT_INVD				13
#define VMX_EXIT_INVLPG				14
#define VMX_EXIT_RDPMC				15
#define VMX_EXIT_RDTSC				16
#define VMX_EXIT_RSM				17
#define VMX_EXIT_VMCALL				18
#define VMX_EXIT_VMCLEAR			19
#define VMX_EXIT_VMLAUNCH			20
#define VMX_EXIT_VMPTRLD			21
#define VMX_EXIT_VMPTRST			22
#define VMX_EXIT_VMREAD				23
#define VMX_EXIT_VMRESUME			24
#define VMX_EXIT_VMWRITE			25
#define VMX_EXIT_VMXOFF				26
#define VMX_EXIT_VMXON				27
#define VMX_EXIT_CR_ACCESS			28
#define VMX_EXIT_MOV_DR				29
#define VMX_EXIT_IO				30
#define VMX_EXIT_RDMSR				31
#define VMX_EXIT_WRMSR				32
#define VMX_EXIT_ENTRY_FAILED_GUEST_STATE	33
#define VMX_EXIT_ENTRY_FAILED_MSR_LOAD		34
#define VMX_EXIT_MWAIT				36
#define VMX_EXIT_MTF				37
#define VMX_EXIT_MONITOR			39
#define VMX_EXIT_PAUSE				40
#define VMX_EXIT_ENTRY_FAILED_MCE		41
#define VMX_EXIT_TPR_BELOW_THRESHOLD		43
#define VMX_EXIT_APIC_ACCESS			44
#define VMX_EXIT_VIRTUALIZED_EOI		45
#define VMX_EXIT_GDTR_IDTR			46
#define	VMX_EXIT_LDTR_TR			47
#define VMX_EXIT_EPT_VIOLATION			48
#define VMX_EXIT_EPT_MISCONFIGURATION		49
#define VMX_EXIT_INVEPT				50
#define VMX_EXIT_RDTSCP				51
#define VMX_EXIT_VMX_PREEMPTION_TIMER_EXPIRED	52
#define VMX_EXIT_INVVPID			53
#define VMX_EXIT_WBINVD				54
#define VMX_EXIT_XSETBV				55
#define VMX_EXIT_APIC_WRITE			56
#define VMX_EXIT_RDRAND				57
#define VMX_EXIT_INVPCID			58
#define VMX_EXIT_VMFUNC				59
#define VMX_EXIT_RDSEED				61
#define VMX_EXIT_XSAVES				63
#define VMX_EXIT_XRSTORS			64

#define VM_EXIT_TERMINATED			0xFFFE
#define VM_EXIT_NONE				0xFFFF

/*
 * VCPU state values. Note that there is a conversion function in vmm.c
 * (vcpu_state_decode) that converts these to human readable strings,
 * so this enum and vcpu_state_decode should be kept in sync.
 */
enum {
	VCPU_STATE_STOPPED,
	VCPU_STATE_RUNNING,
	VCPU_STATE_REQTERM,
	VCPU_STATE_TERMINATED,
	VCPU_STATE_UNKNOWN,
};

enum {
	VEI_DIR_OUT,
	VEI_DIR_IN
};

/*
 * vm exit data
 *  vm_exit_inout		: describes an IN/OUT exit
 */
struct vm_exit_inout {
	uint8_t			vei_size;	/* Size of access */
	uint8_t			vei_dir;	/* Direction */
	uint8_t			vei_rep;	/* REP prefix? */
	uint8_t			vei_string;	/* string variety? */
	uint8_t			vei_encoding;	/* operand encoding */
	uint16_t		vei_port;	/* port */
	uint32_t		vei_data;	/* data (for IN insns) */
};

union vm_exit {
	struct vm_exit_inout	vei;		/* IN/OUT exit */
};

/*
 * struct vcpu_segment_info describes a segment + selector set, used
 * in constructing the initial vcpu register content
 */
struct vcpu_segment_info {
	uint16_t vsi_sel;
	uint32_t vsi_limit;
	uint32_t vsi_ar;
	uint64_t vsi_base;
};

/*
 * struct vcpu_init_state describes the set of vmd-settable registers
 * that the VM's vcpus will be set to during VM boot or reset. Certain
 * registers are always set to 0 (eg, the GP regs) and certain registers
 * have fixed values based on hardware requirements and calculated by
 * vmm (eg, CR0/CR4)
 */
struct vcpu_init_state {
	uint64_t			vis_rflags;
	uint64_t			vis_rip;
	uint64_t			vis_rsp;
	uint64_t			vis_cr0;
	uint64_t			vis_cr3;

	struct vcpu_segment_info	vis_cs;
	struct vcpu_segment_info	vis_ds;
	struct vcpu_segment_info	vis_es;
	struct vcpu_segment_info	vis_fs;
	struct vcpu_segment_info	vis_gs;
	struct vcpu_segment_info	vis_ss;
	struct vcpu_segment_info	vis_gdtr;
	struct vcpu_segment_info	vis_idtr;
	struct vcpu_segment_info	vis_ldtr;
	struct vcpu_segment_info	vis_tr;
};

struct vm_mem_range {
	paddr_t	vmr_gpa;
	vaddr_t vmr_va;
	size_t	vmr_size;
};

struct vm_create_params {
	/* Input parameters to VMM_IOC_CREATE */
	size_t			vcp_nmemranges;
	size_t			vcp_ncpus;
	size_t			vcp_ndisks;
	size_t			vcp_nnics;
	struct vm_mem_range	vcp_memranges[VMM_MAX_MEM_RANGES];
	char			vcp_disks[VMM_MAX_DISKS_PER_VM][VMM_MAX_PATH_DISK];
	char			vcp_name[VMM_MAX_NAME_LEN];
	char			vcp_kernel[VMM_MAX_KERNEL_PATH];
	uint8_t			vcp_macs[VMM_MAX_NICS_PER_VM][6];

	/* Output parameter from VMM_IOC_CREATE */
	uint32_t	vcp_id;
};

struct vm_run_params {
	/* Input parameters to VMM_IOC_RUN */
	uint32_t	vrp_vm_id;
	uint32_t	vrp_vcpu_id;
	uint8_t		vrp_continue;		/* Continuing from an exit */
	int16_t		vrp_injint;		/* Injected interrupt vector */

	/* Input/output parameter to VMM_IOC_RUN */
	union vm_exit	*vrp_exit;		/* updated exit data */

	/* Output parameter from VMM_IOC_RUN */
	uint16_t	vrp_exit_reason;	/* exit reason */
};

struct vm_info_result {
	/* Output parameters from VMM_IOC_INFO */
	size_t		vir_memory_size;
	size_t		vir_used_size;
	size_t		vir_ncpus;
	uint8_t		vir_vcpu_state[VMM_MAX_VCPUS_PER_VM];
	pid_t		vir_creator_pid;
	uint32_t	vir_id;
	char		vir_name[VMM_MAX_NAME_LEN];
};

struct vm_info_params {
	/* Input parameters to VMM_IOC_INFO */
	size_t			vip_size;	/* Output buffer size */

	/* Output Parameters from VMM_IOC_INFO */
	size_t			 vip_info_ct;	/* # of entries returned */
	struct vm_info_result	*vip_info;	/* Output buffer */
};

struct vm_terminate_params {
	/* Input parameters to VMM_IOC_TERM */
	uint32_t		vtp_vm_id;
};

struct vm_resetcpu_params {
	/* Input parameters to VMM_IOC_RESETCPU */
	uint32_t		vrp_vm_id;
	uint32_t		vrp_vcpu_id;
	struct vcpu_init_state	vrp_init_state;
};

struct vm_intr_params {
	/* Input parameters to VMM_IOC_INTR */
	uint32_t		vip_vm_id;
	uint32_t		vip_vcpu_id;
	uint16_t		vip_intr;
};

/* IOCTL definitions */
#define VMM_IOC_CREATE _IOWR('V', 1, struct vm_create_params) /* Create VM */
#define VMM_IOC_RUN _IOWR('V', 2, struct vm_run_params) /* Run VCPU */
#define VMM_IOC_INFO _IOWR('V', 3, struct vm_info_params) /* Get VM Info */
#define VMM_IOC_TERM _IOW('V', 4, struct vm_terminate_params) /* Terminate VM */
#define VMM_IOC_RESETCPU _IOW('V', 5, struct vm_resetcpu_params) /* Reset */
#define VMM_IOC_INTR _IOW('V', 6, struct vm_intr_params) /* Intr pending */

#ifdef _KERNEL

#define VMX_FAIL_LAUNCH_UNKNOWN 1
#define VMX_FAIL_LAUNCH_INVALID_VMCS 2
#define VMX_FAIL_LAUNCH_VALID_VMCS 3

#define VMX_NUM_MSR_STORE 7

enum {
	VMM_MODE_UNKNOWN,
	VMM_MODE_VMX,
	VMM_MODE_EPT,
	VMM_MODE_SVM,
	VMM_MODE_RVI
};

enum {
	VMM_MEM_TYPE_REGULAR,
	VMM_MEM_TYPE_UNKNOWN	
};

/* Forward declarations */
struct vm;

/*
 * Implementation-specific cpu state
 */
struct vmcb {
};

struct vmcs {
	uint32_t	vmcs_revision;
};

struct vmx_invvpid_descriptor
{
	uint64_t	vid_vpid; // : 16;
	uint64_t	vid_addr;
};

struct vmx_invept_descriptor
{
	uint64_t	vid_eptp;
	uint64_t	vid_reserved;
};

struct vmx_msr_store
{
	uint64_t	vms_index : 32;
	uint64_t	vms_data;
};

/*
 * Storage for guest registers not preserved in VMCS and various exit
 * information.
 *
 * Note that vmx_enter_guest depends on the layout of this struct for
 * field access.
 */
struct vmx_gueststate
{
	/* %rsi should be first */
	uint64_t	vg_rsi;			/* 0x00 */
	uint64_t	vg_rax;			/* 0x08 */
	uint64_t	vg_rbx;			/* 0x10 */
	uint64_t	vg_rcx;			/* 0x18 */
	uint64_t	vg_rdx;			/* 0x20 */
	uint64_t	vg_rdi;			/* 0x28 */
	uint64_t	vg_rbp;			/* 0x30 */
	uint64_t	vg_r8;			/* 0x38 */
	uint64_t	vg_r9;			/* 0x40 */
	uint64_t	vg_r10;			/* 0x48 */
	uint64_t	vg_r11;			/* 0x50 */
	uint64_t	vg_r12;			/* 0x58 */
	uint64_t	vg_r13;			/* 0x60 */
	uint64_t	vg_r14;			/* 0x68 */
	uint64_t	vg_r15;			/* 0x70 */
	uint64_t	vg_cr2;			/* 0x78 */
	uint64_t	vg_rip;			/* 0x80 */
	uint32_t	vg_exit_reason;		/* 0x88 */
};

/*
 * Virtual Machine
 */
struct vm;

/*
 * Virtual CPU
 */
struct vcpu {
	/* VMCS / VMCB pointer */
	vaddr_t vc_control_va;
	uint64_t vc_control_pa;

	/* VLAPIC pointer */
	vaddr_t vc_vlapic_va;
	uint64_t vc_vlapic_pa;

	/* MSR bitmap address */
	vaddr_t vc_msr_bitmap_va;
	uint64_t vc_msr_bitmap_pa;

	struct vm *vc_parent;
	uint32_t vc_id;
	u_int vc_state;
	SLIST_ENTRY(vcpu) vc_vcpu_link;
	vaddr_t vc_hsa_stack_va;

	uint8_t vc_virt_mode;

	struct cpu_info *vc_last_pcpu;
	union vm_exit vc_exit;

	uint16_t vc_intr;

	/* VMX only */
	uint64_t vc_vmx_basic;
	uint64_t vc_vmx_entry_ctls;
	uint64_t vc_vmx_true_entry_ctls;
	uint64_t vc_vmx_exit_ctls;
	uint64_t vc_vmx_true_exit_ctls;
	uint64_t vc_vmx_pinbased_ctls;
	uint64_t vc_vmx_true_pinbased_ctls;
	uint64_t vc_vmx_procbased_ctls;
	uint64_t vc_vmx_true_procbased_ctls;
	uint64_t vc_vmx_procbased2_ctls;
	struct vmx_gueststate vc_gueststate;
	vaddr_t vc_vmx_msr_exit_save_va;
	paddr_t vc_vmx_msr_exit_save_pa;
	vaddr_t vc_vmx_msr_exit_load_va;
	paddr_t vc_vmx_msr_exit_load_pa;
	vaddr_t vc_vmx_msr_entry_load_va;
	paddr_t vc_vmx_msr_entry_load_pa;
};

SLIST_HEAD(vcpu_head, vcpu);

void	vmm_dispatch_intr(vaddr_t);
int	vmxon(uint64_t *);
int	vmxoff(void);
int	vmclear(uint64_t *);
int	vmptrld(uint64_t *);
int	vmptrst(uint64_t *);
int	vmwrite(uint64_t, uint64_t);
int	vmread(uint64_t, uint64_t *);
void	invvpid(uint64_t, struct vmx_invvpid_descriptor *);
void	invept(uint64_t, struct vmx_invept_descriptor *);
int	vmx_enter_guest(uint64_t *, struct vmx_gueststate *, int);
void	start_vmm_on_cpu(struct cpu_info *);
void	stop_vmm_on_cpu(struct cpu_info *);

#endif /* _KERNEL */

#endif /* ! _MACHINE_VMMVAR_H_ */
