#include "kvm/kvm-cpu.h"
#include "kvm/kvm.h"
#include "kvm/virtio.h"

#include <asm/ptrace.h>

#define ARM_CORE_REG(x)	(KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_CORE | \
			 KVM_REG_ARM_CORE_REG(x))

unsigned long kvm_cpu__get_vcpu_mpidr(struct kvm_cpu *vcpu)
{
	struct kvm_one_reg reg;
	u32 mpidr;

	reg.id = ARM_CP15_REG32(ARM_CPU_ID, ARM_CPU_ID_MPIDR);
	reg.addr = (u64)(unsigned long)&mpidr;
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (get_mpidr vcpu%ld", vcpu->cpu_id);

	return mpidr;
}

void kvm_cpu__reset_vcpu(struct kvm_cpu *vcpu)
{
	struct kvm *kvm	= vcpu->kvm;
	struct kvm_one_reg reg;
	u32 data;

	/* Who said future-proofing was a good idea? */
	reg.addr = (u64)(unsigned long)&data;

	/* cpsr = IRQs/FIQs masked */
	data	= PSR_I_BIT | PSR_F_BIT | SVC_MODE;
	reg.id	= ARM_CORE_REG(usr_regs.ARM_cpsr);
	if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
		die_perror("KVM_SET_ONE_REG failed (cpsr)");

	/* Secondary cores are stopped awaiting PSCI wakeup */
	if (vcpu->cpu_id != 0)
		return;

	/* r0 = 0 */
	data	= 0;
	reg.id	= ARM_CORE_REG(usr_regs.ARM_r0);
	if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
		die_perror("KVM_SET_ONE_REG failed (r0)");

	/* r1 = machine type (-1) */
	data	= -1;
	reg.id	= ARM_CORE_REG(usr_regs.ARM_r1);
	if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
		die_perror("KVM_SET_ONE_REG failed (r1)");

	/* r2 = physical address of the device tree blob */
	data	= kvm->arch.dtb_guest_start;
	reg.id	= ARM_CORE_REG(usr_regs.ARM_r2);
	if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
		die_perror("KVM_SET_ONE_REG failed (r2)");

	/* pc = start of kernel image */
	data	= kvm->arch.kern_guest_start;
	reg.id	= ARM_CORE_REG(usr_regs.ARM_pc);
	if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, &reg) < 0)
		die_perror("KVM_SET_ONE_REG failed (pc)");
}

int kvm_cpu__get_endianness(struct kvm_cpu *vcpu)
{
	struct kvm_one_reg reg;
	u32 data;

	reg.id = ARM_CORE_REG(usr_regs.ARM_cpsr);
	reg.addr = (u64)(unsigned long)&data;
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (cpsr)");

	return (data & PSR_E_BIT) ? VIRTIO_ENDIAN_BE : VIRTIO_ENDIAN_LE;
}

void kvm_cpu__show_code(struct kvm_cpu *vcpu)
{
	struct kvm_one_reg reg;
	u32 data;
	int debug_fd = kvm_cpu__get_debug_fd();

	reg.addr = (u64)(unsigned long)&data;

	dprintf(debug_fd, "\n*pc:\n");
	reg.id = ARM_CORE_REG(usr_regs.ARM_pc);
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (show_code @ PC)");

	kvm__dump_mem(vcpu->kvm, data, 32, debug_fd);

	dprintf(debug_fd, "\n*lr (svc):\n");
	reg.id = ARM_CORE_REG(svc_regs[1]);
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (show_code @ LR_svc)");
	data &= ~0x1;

	kvm__dump_mem(vcpu->kvm, data, 32, debug_fd);
}

void kvm_cpu__show_registers(struct kvm_cpu *vcpu)
{
	struct kvm_one_reg reg;
	u32 data;
	int debug_fd = kvm_cpu__get_debug_fd();

	reg.addr	= (u64)(unsigned long)&data;
	dprintf(debug_fd, "\n Registers:\n");

	reg.id		= ARM_CORE_REG(usr_regs.ARM_pc);
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (pc)");
	dprintf(debug_fd, " PC:    0x%x\n", data);

	reg.id		= ARM_CORE_REG(usr_regs.ARM_cpsr);
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (cpsr)");
	dprintf(debug_fd, " CPSR:  0x%x\n", data);

	reg.id		= ARM_CORE_REG(svc_regs[0]);
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (SP_svc)");
	dprintf(debug_fd, " SP_svc:  0x%x\n", data);

	reg.id		= ARM_CORE_REG(svc_regs[1]);
	if (ioctl(vcpu->vcpu_fd, KVM_GET_ONE_REG, &reg) < 0)
		die("KVM_GET_ONE_REG failed (LR_svc)");
	dprintf(debug_fd, " LR_svc:  0x%x\n", data);
}
