/*
 * Copyright (c) 2007, 2008 University of Tsukuba
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the University of Tsukuba nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 2010-2014 Yuichi Watanabe
 */

#ifndef _CORE_VT_INTERNAL_H
#define _CORE_VT_INTERNAL_H

#include <core/types.h>
#include "asm.h"
#include "constants.h"
#include "regs.h"
#include "cpu_seg.h"

struct vcpu;

enum exit_qual_cr_lmsw {
	EXIT_QUAL_CR_LMSW_REGISTER = 0,
	EXIT_QUAL_CR_LMSW_MEMORY = 1,
};

enum exit_qual_cr_type {
	EXIT_QUAL_CR_TYPE_MOV_TO_CR = 0,
	EXIT_QUAL_CR_TYPE_MOV_FROM_CR = 1,
	EXIT_QUAL_CR_TYPE_CLTS = 2,
	EXIT_QUAL_CR_TYPE_LMSW = 3,
};

enum exit_qual_io_dir {
	EXIT_QUAL_IO_DIR_OUT = 0,
	EXIT_QUAL_IO_DIR_IN = 1,
};

enum exit_qual_io_op {
	EXIT_QUAL_IO_OP_DX = 0,
	EXIT_QUAL_IO_OP_IMMEDIATE = 1,
};

enum exit_qual_io_rep {
	EXIT_QUAL_IO_REP_NOT_REP = 0,
	EXIT_QUAL_IO_REP_REP = 1,
};

enum exit_qual_io_size {
	EXIT_QUAL_IO_SIZE_1BYTE = 0,
	EXIT_QUAL_IO_SIZE_2BYTE = 1,
	EXIT_QUAL_IO_SIZE_4BYTE = 3,
};

enum exit_qual_io_str {
	EXIT_QUAL_IO_STR_NOT_STRING = 0,
	EXIT_QUAL_IO_STR_STRING = 1,
};

enum exit_qual_ts_src {
	EXIT_QUAL_TS_SRC_CALL = 0,
	EXIT_QUAL_TS_SRC_IRET = 1,
	EXIT_QUAL_TS_SRC_JMP = 2,
	EXIT_QUAL_TS_SRC_INTR = 3,
};

struct regs_in_vmcs_sreg {
	u16 sel;
	ulong limit;
	ulong acr;
	ulong base;
};

struct regs_in_vmcs_desc {
	ulong base;
	ulong limit;
};

struct regs_in_vmcs {
	struct regs_in_vmcs_sreg es, cs, ss, ds, fs, gs, ldtr, tr;
	struct regs_in_vmcs_desc gdtr, idtr;
	ulong cr0, cr3, cr4;
	ulong dr7, rflags;
};

struct exit_qual_cr {
	enum control_reg num : 4;
	enum exit_qual_cr_type type : 2;
	enum exit_qual_cr_lmsw lmsw : 1;
	unsigned int reserved1 : 1;
	enum general_reg reg : 4;
	unsigned int reserved2 : 4;
	unsigned int lmsw_src : 16;
} __attribute__ ((packed));

struct exit_qual_io {
	enum exit_qual_io_size size : 3;
	enum exit_qual_io_dir dir : 1;
	enum exit_qual_io_str str : 1;
	enum exit_qual_io_rep rep : 1;
	enum exit_qual_io_op op : 1;
	unsigned int reserved1 : 9;
	unsigned int port : 16;
} __attribute__ ((packed));

struct exit_qual_ts {
	unsigned int sel : 16;
	unsigned int reserved1 : 14;
	enum exit_qual_ts_src src : 2;
	unsigned int reserved2 : 32;
};

void vt_reset (void);
void vt_vminit (void);
void vt_vmexit (void);
void shutdown_vmm (void);
void printexitreason (int num);
char *message_exitreason (int num);
void vt_io (void);
void vt_iopass_init (void);
void vt_extern_iopass (struct vcpu *p, ioport_t port, bool pass);
void vt_invlpg (ulong addr);
void vt_run_vm (void);
void vt_msr_update_lma (void);
int vt_add_guest_msr (ulong index);
bool vt_read_guest_msr (int i, u64 *data);
bool vt_write_guest_msr (int i, u64 data);
bool vt_read_msr (u32 msrindex, u64 *msrdata);
bool vt_write_msr (u32 msrindex, u64 msrdata);
void vt_msr_init (void);
void vt_panic_dump (void);
void vt_get_current_regs_in_vmcs (struct regs_in_vmcs *p);
void vt_read_general_reg (enum general_reg reg, ulong *val);
void vt_write_general_reg (enum general_reg reg, ulong val);
void vt_read_control_reg (enum control_reg reg, ulong *val);
void vt_write_control_reg (enum control_reg reg, ulong val);
void vt_read_sreg_sel (enum sreg s, u16 *val);
void vt_read_sreg_acr (enum sreg s, ulong *val);
void vt_read_sreg_base (enum sreg s, ulong *val);
void vt_read_sreg_limit (enum sreg s, ulong *val);
void vt_spt_setcr3 (ulong cr3);
void vt_read_ip (ulong *val);
void vt_write_ip (ulong val);
void vt_read_flags (ulong *val);
void vt_write_flags (ulong val);
void vt_read_gdtr (ulong *base, ulong *limit);
void vt_write_gdtr (ulong base, ulong limit);
void vt_read_idtr (ulong *base, ulong *limit);
void vt_write_idtr (ulong base, ulong limit);
void vt_write_realmode_seg (enum sreg s, u16 val);
vmmerr_t vt_writing_sreg (enum sreg s);
void vt_extint_pending (bool pending);
void vt_vm_entry_check(void);

#ifdef CONFIG_VT_EPT
void vt_ept_init(void);
void vt_ept_violation(void);
void vt_ept_pg_change(bool pg);
#else
static inline vmmerr_t
vt_ept_init(void)
{
	return VMMERR_NOSUP;
}
static inline void
vt_ept_violation(void)
{
}
static inline void
vt_ept_pg_change(bool pe)
{
}
#endif

static inline void
add_ip (void)
{
	ulong ip, len;

	asm_vmread (VMCS_GUEST_RIP, &ip);
	asm_vmread (VMCS_VMEXIT_INSTRUCTION_LEN, &len);
	ip += len;
	asm_vmwrite (VMCS_GUEST_RIP, ip);
}

#endif
