/*
 * 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-2013 Yuichi Watanabe
 */

#include <common/common.h>
#include <core/initfunc.h>
#include <core/spinlock.h>
#include <core/string.h>
#include "apic.h"
#include "apic_pass.h"
#include "current.h"
#include "mm.h"
#include "pcpu.h"
#include <core/types.h>
#include "vcpu.h"

/* call func with every vcpu */
/* return immediately if func returns true */
/* q is a pointer for any purpose */
void
vcpu_list_foreach (struct vm *vm, bool (*func) (struct vcpu *p, void *q),
		   void *q)
{
	struct vcpu *p, *pn;

	spinlock_lock(&vm->vcpu_list_lock);
	LIST2_FOREACH_DELETABLE(vm->vcpu_list, vcpu_list, p, pn) {
		spinlock_unlock(&vm->vcpu_list_lock);
		if (func (p, q)) {
			break;
		}
		spinlock_lock(&vm->vcpu_list_lock);
	}
	spinlock_unlock(&vm->vcpu_list_lock);
}

struct vcpu *
find_vcpu_with_apic_id (struct vm *vm, apic_id_t apic_id)
{
	struct vcpu *p;

	spinlock_lock(&vm->vcpu_list_lock);
	LIST2_FOREACH(vm->vcpu_list, vcpu_list, p) {
		if (p->apic_id == apic_id) {
			spinlock_unlock(&vm->vcpu_list_lock);
			return p;
		}
	}
	spinlock_unlock(&vm->vcpu_list_lock);
	return NULL;
}

#include <core/printf.h>

struct vcpu *
new_vcpu (struct vm *vm, bool vbsp)
{
	current = (struct vcpu *)alloc(sizeof (struct vcpu));
	if (current == NULL) {
		return current;
	}
	memset(current, 0, sizeof (struct vcpu));
	current->vbsp = vbsp;
	current->pcpu = currentcpu;
	current->apic_id = currentcpu->apic_id;
	current->vm = vm;

	int_init_hook();

	spinlock_lock(&vm->vcpu_list_lock);
	LIST2_ADD(vm->vcpu_list, vcpu_list, current);
	vm->vcpu_count++;
	spinlock_unlock(&vm->vcpu_list_lock);

	return current;
}

struct vcpu *
vcpu_next (struct vm *vm, struct vcpu *vcpu)
{
	struct vcpu *next;
	spinlock_lock(&vm->vcpu_list_lock);
	next = LIST2_NEXT(vm->vcpu_list, vcpu_list, vcpu);
	spinlock_unlock(&vm->vcpu_list_lock);
	return next;
}

void
vcpu_reset(void)
{
	current->vmctl.reset ();
	current->vmctl.write_general_reg (GENERAL_REG_RAX, 0);
	current->vmctl.write_general_reg (GENERAL_REG_RCX, 0);
	current->vmctl.write_general_reg (GENERAL_REG_RDX, 0);
	current->vmctl.write_general_reg (GENERAL_REG_RBX, 0);
	current->vmctl.write_general_reg (GENERAL_REG_RSP, 0);
	current->vmctl.write_general_reg (GENERAL_REG_RBP, 0);
	current->vmctl.write_general_reg (GENERAL_REG_RSI, 0);
	current->vmctl.write_general_reg (GENERAL_REG_RDI, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R8, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R9, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R10, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R11, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R12, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R13, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R14, 0);
	current->vmctl.write_general_reg (GENERAL_REG_R15, 0);
	current->vmctl.write_flags (RFLAGS_ALWAYS1_BIT);
	current->vmctl.write_realmode_seg (SREG_ES, 0);
	current->vmctl.write_realmode_seg (SREG_CS, 0xf000);
	current->vmctl.write_realmode_seg (SREG_SS, 0);
	current->vmctl.write_realmode_seg (SREG_DS, 0);
	current->vmctl.write_realmode_seg (SREG_FS, 0);
	current->vmctl.write_realmode_seg (SREG_GS, 0);
	current->vmctl.write_control_reg (CONTROL_REG_CR0, CR0_ET_BIT);
	current->vmctl.write_control_reg (CONTROL_REG_CR2, 0);
	current->vmctl.write_control_reg (CONTROL_REG_CR3, 0);
	current->vmctl.write_control_reg (CONTROL_REG_CR4, 0);
	current->vmctl.write_ip (0xfff0);
	current->vmctl.write_idtr (0, 0x3FF);
	current->vmctl.write_gdtr (0, 0xFFFF);

	if (!current->vbsp) {
		apic_enter_wait_for_sipi_state();
	}
}
