/*
 * Copyright (c) 2013 Jonas 'Sortie' Termansen.
 *
 * 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.
 *
 * sortix/kernel/cpuid.h
 * Interface to using the cpuid instruction.
 */

#ifndef _INCLUDE_SORTIX_KERNEL_CPUID_H
#define _INCLUDE_SORTIX_KERNEL_CPUID_H

#include <stdint.h>

namespace Sortix {

#if defined(__i386__) || defined(__x86_64__)

__attribute__((const))
__attribute__((unused))
static inline bool IsCPUIdSupported()
{
#if defined(__x86_64__)
	// TODO: Isn't this always supported under x86_64?
	uint64_t id_supported;
	asm ("pushfq\n\t"
	     "popq %%rax             /* Get EFLAGS into EAX */\n\t"
	     "movq %%rax, %%rcx      /* Save original flags in ECX */\n\t"
	     "xorq $0x200000, %%rax  /* Flip ID bit in EFLAGS */\n\t"
	     "pushq %%rax            /* Store modified EFLAGS on stack */\n\t"
	     "popfq                  /* Replace current EFLAGS */\n\t"
	     "pushfq                 /* Read back the EFLAGS */\n\t"
	     "popq %%rax             /* Get EFLAGS into EAX */\n\t"
	     "xorq %%rcx, %%rax      /* Check if flag could be modified */\n\t"
	     : "=a" (id_supported)
	     : /* No inputs.  */
	     : /* Clobbered:  */ "%rcx");
#elif  defined(__i386__)
	uint32_t id_supported;
	asm ("pushfl\n\t"
	     "popl %%eax             /* Get EFLAGS into EAX */\n\t"
	     "movl %%eax, %%ecx      /* Save original flags in ECX */\n\t"
	     "xorl $0x200000, %%eax  /* Flip ID bit in EFLAGS */\n\t"
	     "pushl %%eax            /* Store modified EFLAGS on stack */\n\t"
	     "popfl                  /* Replace current EFLAGS */\n\t"
	     "pushfl                 /* Read back the EFLAGS */\n\t"
	     "popl %%eax             /* Get EFLAGS into EAX */\n\t"
	     "xorl %%ecx, %%eax      /* Check if flag could be modified */\n\t"
	     : "=a" (id_supported)
	     : /* No inputs.  */
	     : /* Clobbered:  */ "%rcx");
#endif
	return id_supported != 0;
}

#define cpuid(num,a,b,c,d) \
  asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \
                : "=a" (a), "=r" (b), "=c" (c), "=d" (d)  \
                : "0" (num))

#endif

} // namespace Sortix

#endif
