/*
 * \brief  x86_64 CPU driver for core
 * \author Adrian-Ken Rueegsegger
 * \author Martin stein
 * \author Reto Buerki
 * \date   2015-02-06
 */

/*
 * Copyright (C) 2015 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _CORE__INCLUDE__SPEC__X86_64__CPU_SUPPORT_H_
#define _CORE__INCLUDE__SPEC__X86_64__CPU_SUPPORT_H_

/* Genode includes */
#include <util/register.h>
#include <kernel/interface_support.h>
#include <cpu/cpu_state.h>

/* base includes */
#include <base/internal/unmanaged_singleton.h>

/* core includes */
#include <fpu.h>
#include <gdt.h>
#include <idt.h>
#include <tss.h>
#include <timer.h>

extern int _mt_idt;
extern int _mt_tss;

namespace Genode { class Cpu; }


class Genode::Cpu
{
	public:

		struct Pd {};

		struct Cr0; /* Control register 0 */
		struct Cr2; /* Control register 2 */
		struct Cr3; /* Control register 3 */
		struct Cr4; /* Control register 4 */

		/**
		 * Extend basic CPU state by members relevant for 'base-hw' only
		 */
		struct Context : Cpu_state
		{
			/**
			 * Address of top-level paging structure.
			 */
			addr_t cr3;

			/**
			 * Return base of assigned translation table
			 */
			addr_t translation_table() const { return cr3; }

			/**
			 * Initialize context
			 *
			 * \param table  physical base of appropriate translation table
			 * \param core   whether it is a core thread or not
			 */
			void init(addr_t const table, bool core);
		};


		/**
		 * An usermode execution state
		 */
		struct User_context : Context, Fpu::Context
		{
			/**
			 * Support for kernel calls
			 */
			void user_arg_0(Kernel::Call_arg const arg) { rdi = arg; }
			void user_arg_1(Kernel::Call_arg const arg) { rsi = arg; }
			void user_arg_2(Kernel::Call_arg const arg) { rdx = arg; }
			void user_arg_3(Kernel::Call_arg const arg) { rcx = arg; }
			void user_arg_4(Kernel::Call_arg const arg) { r8 = arg; }
			void user_arg_5(Kernel::Call_arg const arg) { r9 = arg; }
			void user_arg_6(Kernel::Call_arg const arg) { r10 = arg; }
			void user_arg_7(Kernel::Call_arg const arg) { r11 = arg; }
			Kernel::Call_arg user_arg_0() const { return rdi; }
			Kernel::Call_arg user_arg_1() const { return rsi; }
			Kernel::Call_arg user_arg_2() const { return rdx; }
			Kernel::Call_arg user_arg_3() const { return rcx; }
			Kernel::Call_arg user_arg_4() const { return r8; }
			Kernel::Call_arg user_arg_5() const { return r9; }
			Kernel::Call_arg user_arg_6() const { return r10; }
			Kernel::Call_arg user_arg_7() const { return r11; }
		};

	protected:

		Fpu   _fpu;
		Idt * _idt;
		Tss * _tss;

	public:

		Cpu();

		Fpu & fpu() { return _fpu; }

		static constexpr addr_t exception_entry = 0xffff0000;
		static constexpr addr_t mtc_size        = 1 << 13;

		/**
		 * Wait for the next interrupt as cheap as possible
		 */
		static void wait_for_interrupt() { asm volatile ("pause"); }

		/**
		 * Return wether to retry an undefined user instruction after this call
		 */
		bool retry_undefined_instr(Context&) { return false; }


		/**
		 * Return kernel name of the executing CPU
		 */
		static unsigned executing_id() { return 0; }

		/**
		 * Return kernel name of the primary CPU
		 */
		static unsigned primary_id() { return 0; }

		/**
		 * Switch to new context
		 *
		 * \param context  next CPU context
		 */
		void switch_to(User_context &context) { _fpu.switch_to(context); }
};


struct Genode::Cpu::Cr0 : Register<64>
{
	struct Pe : Bitfield< 0, 1> { }; /* Protection Enable   */
	struct Mp : Bitfield< 1, 1> { }; /* Monitor Coprocessor */
	struct Em : Bitfield< 2, 1> { }; /* Emulation           */
	struct Ts : Bitfield< 3, 1> { }; /* Task Switched       */
	struct Et : Bitfield< 4, 1> { }; /* Extension Type      */
	struct Ne : Bitfield< 5, 1> { }; /* Numeric Error       */
	struct Wp : Bitfield<16, 1> { }; /* Write Protect       */
	struct Am : Bitfield<18, 1> { }; /* Alignment Mask      */
	struct Nw : Bitfield<29, 1> { }; /* Not Write-through   */
	struct Cd : Bitfield<30, 1> { }; /* Cache Disable       */
	struct Pg : Bitfield<31, 1> { }; /* Paging              */

	static void write(access_t const v) {
		asm volatile ("mov %0, %%cr0" :: "r" (v) : ); }

	static access_t read()
	{
		access_t v;
		asm volatile ("mov %%cr0, %0" : "=r" (v) :: );
		return v;
	}
};


/**
 * Control register 2: Page-fault linear address
 *
 * See Intel SDM Vol. 3A, section 2.5.
 */
struct Genode::Cpu::Cr2 : Register<64>
{
	struct Addr : Bitfield<0, 63> { };

	static access_t read()
	{
		access_t v;
		asm volatile ("mov %%cr2, %0" : "=r" (v) :: );
		return v;
	}
};


/**
 * Control register 3: Page-Directory base register
 *
 * See Intel SDM Vol. 3A, section 2.5.
 */
struct Genode::Cpu::Cr3 : Register<64>
{
	struct Pwt : Bitfield<3,1> { };    /* Page-level write-through    */
	struct Pcd : Bitfield<4,1> { };    /* Page-level cache disable    */
	struct Pdb : Bitfield<12, 36> { }; /* Page-directory base address */

	static void write(access_t const v) {
		asm volatile ("mov %0, %%cr3" :: "r" (v) : ); }

	static access_t read()
	{
		access_t v;
		asm volatile ("mov %%cr3, %0" : "=r" (v) :: );
		return v;
	}

	/**
	 * Return initialized value
	 *
	 * \param table  base of targeted translation table
	 */
	static access_t init(addr_t const table) {
		return Pdb::masked(table); }
};


struct Genode::Cpu::Cr4 : Register<64>
{
	struct Vme        : Bitfield< 0, 1> { }; /* Virtual-8086 Mode Extensions */
	struct Pvi        : Bitfield< 1, 1> { }; /* Protected-Mode Virtual IRQs */
	struct Tsd        : Bitfield< 2, 1> { }; /* Time Stamp Disable */
	struct De         : Bitfield< 3, 1> { }; /* Debugging Exceptions */
	struct Pse        : Bitfield< 4, 1> { }; /* Page Size Extensions */
	struct Pae        : Bitfield< 5, 1> { }; /* Physical Address Extension */
	struct Mce        : Bitfield< 6, 1> { }; /* Machine-Check Enable */
	struct Pge        : Bitfield< 7, 1> { }; /* Page Global Enable */
	struct Pce        : Bitfield< 8, 1> { }; /* Performance-Monitoring Counter
	                                            Enable*/
	struct Osfxsr     : Bitfield< 9, 1> { }; /* OS Support for FXSAVE and
	                                            FXRSTOR instructions*/
	struct Osxmmexcpt : Bitfield<10, 1> { }; /* OS Support for Unmasked
	                                            SIMD/FPU Exceptions */
	struct Vmxe       : Bitfield<13, 1> { }; /* VMX Enable */
	struct Smxe       : Bitfield<14, 1> { }; /* SMX Enable */
	struct Fsgsbase   : Bitfield<16, 1> { }; /* FSGSBASE-Enable */
	struct Pcide      : Bitfield<17, 1> { }; /* PCIDE Enable */
	struct Osxsave    : Bitfield<18, 1> { }; /* XSAVE and Processor Extended
	                                            States-Enable */
	struct Smep       : Bitfield<20, 1> { }; /* SMEP Enable */
	struct Smap       : Bitfield<21, 1> { }; /* SMAP Enable */

	static void write(access_t const v) {
		asm volatile ("mov %0, %%cr4" :: "r" (v) : ); }

	static access_t read()
	{
		access_t v;
		asm volatile ("mov %%cr4, %0" : "=r" (v) :: );
		return v;
	}
};

#endif /* _CORE__INCLUDE__SPEC__X86_64__CPU_SUPPORT_H_ */
