#define _GNU_SOURCE
#include <stdint.h>
#include <stddef.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <cpuid.h>
#include "catalejo-macro.h"
#include "catalejo-section.h"
#include "catalejo-fault.h"
#include "catalejo-fixup.h"
static atomic_int catalejo_monitor_backend = -1;
#define CATALEJO_MONITOR_WAIT_CYCLES 65536
#define CATALEJO_CPUID_WAITPKG (1U << 5)
#define CATALEJO_CPUID_MONITORX (1U << 29)
void catalejo_fault_routines_retain(void)
{
}
static catalejo_monitor_backend_t catalejo_monitor_detect()
{
unsigned int target_eax;
unsigned int target_ebx;
unsigned int target_ecx;
unsigned int target_edx;
if (__get_cpuid_max(0, NULL) >= 7 &&
__get_cpuid_count(7, 0, &target_eax, &target_ebx, &target_ecx, &target_edx) &&
(target_ecx & CATALEJO_CPUID_WAITPKG) != 0)
return CATALEJO_MONITOR_BACKEND_INTEL_UMONITOR;
if (__get_cpuid_max(0x80000000, NULL) >= 0x80000001 &&
__get_cpuid(0x80000001, &target_eax, &target_ebx, &target_ecx, &target_edx) &&
(target_ecx & CATALEJO_CPUID_MONITORX) != 0)
return CATALEJO_MONITOR_BACKEND_AMD_MONITORX;
return CATALEJO_MONITOR_BACKEND_UNSUPPORTED;
}
catalejo_monitor_backend_t catalejo_monitor_select()
{
int target_backend = atomic_load_explicit(&catalejo_monitor_backend, memory_order_acquire);
if (target_backend >= 0)
return (catalejo_monitor_backend_t)target_backend;
int detected_backend = catalejo_monitor_detect();
int expected_backend = -1;
if (atomic_compare_exchange_strong_explicit(&catalejo_monitor_backend, &expected_backend,
detected_backend, memory_order_acq_rel,
memory_order_acquire))
return (catalejo_monitor_backend_t)detected_backend;
return (catalejo_monitor_backend_t)expected_backend;
}
static void catalejo_monitor_downgrade(catalejo_monitor_backend_t target_backend)
{
int expected_backend = target_backend;
atomic_compare_exchange_strong_explicit(&catalejo_monitor_backend, &expected_backend,
CATALEJO_MONITOR_BACKEND_UNSUPPORTED,
memory_order_acq_rel, memory_order_acquire);
}
catalejo_monitor_arm_outcome_t catalejo_monitor_arm(const uint8_t *target_address)
{
catalejo_monitor_backend_t target_backend = catalejo_monitor_select();
catalejo_faultable_instruction_outcome_t target_outcome;
switch (target_backend) {
case CATALEJO_MONITOR_BACKEND_INTEL_UMONITOR:
target_outcome = catalejo_monitor_intel_arm(target_address);
break;
case CATALEJO_MONITOR_BACKEND_AMD_MONITORX:
target_outcome = catalejo_monitor_amd_arm(target_address);
break;
case CATALEJO_MONITOR_BACKEND_UNSUPPORTED:
return CATALEJO_MONITOR_ARM_UNSUPPORTED;
default:
return CATALEJO_MONITOR_ARM_UNSUPPORTED;
}
if (target_outcome.outcome_status == CATALEJO_OUTCOME_SUCCESS)
return target_backend == CATALEJO_MONITOR_BACKEND_INTEL_UMONITOR ?
CATALEJO_MONITOR_ARM_INTEL_UMONITOR :
CATALEJO_MONITOR_ARM_AMD_MONITORX;
if (target_outcome.except_code == MIRILLA_EXCEPT_X86_INVALID_OPCODE) {
catalejo_monitor_downgrade(target_backend);
return CATALEJO_MONITOR_ARM_UNSUPPORTED;
}
return CATALEJO_MONITOR_ARM_FAULT;
}
catalejo_outcome_t catalejo_monitor_wait(catalejo_monitor_backend_t target_backend)
{
if (target_backend != catalejo_monitor_select())
return CATALEJO_OUTCOME_INVALID_VALUE;
catalejo_faultable_instruction_outcome_t target_outcome;
switch (target_backend) {
case CATALEJO_MONITOR_BACKEND_INTEL_UMONITOR:
target_outcome = catalejo_monitor_intel_wait();
break;
case CATALEJO_MONITOR_BACKEND_AMD_MONITORX:
target_outcome = catalejo_monitor_amd_wait();
break;
case CATALEJO_MONITOR_BACKEND_UNSUPPORTED:
return CATALEJO_OUTCOME_INVALID_VALUE;
default:
return CATALEJO_OUTCOME_INVALID_VALUE;
}
if (target_outcome.outcome_status == CATALEJO_OUTCOME_SUCCESS)
return CATALEJO_OUTCOME_SUCCESS;
catalejo_monitor_downgrade(target_backend);
return CATALEJO_OUTCOME_INVALID_VALUE;
}
#define X(target_typename, target_type, target_mnemonic, target_register, target_register32) \
CATALEJO_FAULT_ROUTINE catalejo_outcome_t \
CATALEJO_CONCAT(catalejo_read_, target_typename)(CATALEJO_UNUSED const target_type *target_source, \
CATALEJO_UNUSED target_type *target_value) \
{ \
__asm__ volatile( \
\
\
"xorl %" #target_register32 ", %" #target_register32 "\n\t" \
\
\
"1:\n\t" \
#target_mnemonic " (%rdi), %" #target_register "\n\t" \
#target_mnemonic " %" #target_register ", (%rsi)\n\t" \
"2:\n\t" \
\
\
"xorl %eax, %eax\n\t" \
"ret\n\t" \
\
\
"3:\n\t" \
"movl $" CATALEJO_STR(CATALEJO_OUTCOME_ERROR_VALUE) ", %eax\n\t" \
"ret\n\t" \
\
CATALEJO_ROLLBACK_RECORD("1b", "2b", "3b", CATALEJO_FAULT_EXCEPTION_MEMORY)); \
}
CATALEJO_FAULT_ROUTINE_SPECIFICATION
#undef X
#define X(target_typename, target_type, target_mnemonic, target_register, target_register32) \
CATALEJO_FAULT_ROUTINE catalejo_outcome_t \
CATALEJO_CONCAT(catalejo_write_, target_typename)(CATALEJO_UNUSED target_type *target_value, \
CATALEJO_UNUSED const target_type *target_source) \
{ \
__asm__ volatile( \
\
\
"xorl %" #target_register32 ", %" #target_register32 "\n\t" \
\
\
"1:\n\t" \
#target_mnemonic " (%rsi), %" #target_register "\n\t" \
#target_mnemonic " %" #target_register ", (%rdi)\n\t" \
"2:\n\t" \
\
\
"xorl %eax, %eax\n\t" \
"ret\n\t" \
\
\
"3:\n\t" \
"movl $" CATALEJO_STR(CATALEJO_OUTCOME_ERROR_VALUE) ", %eax\n\t" \
"ret\n\t" \
\
CATALEJO_ROLLBACK_RECORD("1b", "2b", "3b", CATALEJO_FAULT_EXCEPTION_MEMORY)); \
}
CATALEJO_FAULT_ROUTINE_SPECIFICATION
#undef X
CATALEJO_FAULT_ROUTINE catalejo_faultable_instruction_outcome_t
catalejo_monitor_intel_arm(CATALEJO_UNUSED const uint8_t *target_address)
{
__asm__ volatile(
"movq %rdi, %rax\n\t"
"1:\n\t"
"umonitor %rax\n\t"
"2:\n\t"
"xorl %eax, %eax\n\t"
"xorl %edx, %edx\n\t"
"ret\n\t"
"3:\n\t"
"movl $" CATALEJO_STR(CATALEJO_OUTCOME_ERROR_VALUE) ", %eax\n\t"
"ret\n\t"
CATALEJO_MONITOR_RECOVERY);
}
CATALEJO_FAULT_ROUTINE catalejo_faultable_instruction_outcome_t catalejo_monitor_intel_wait()
{
__asm__ volatile(
"rdtsc\n\t"
"addl $" CATALEJO_STR(CATALEJO_MONITOR_WAIT_CYCLES) ", %eax\n\t"
"adcl $0, %edx\n\t"
"xorl %ecx, %ecx\n\t"
"1:\n\t"
"umwait %ecx\n\t"
"2:\n\t"
"xorl %eax, %eax\n\t"
"xorl %edx, %edx\n\t"
"ret\n\t"
"3:\n\t"
"movl $" CATALEJO_STR(CATALEJO_OUTCOME_ERROR_VALUE) ", %eax\n\t"
"ret\n\t"
CATALEJO_MONITOR_RECOVERY);
}
CATALEJO_FAULT_ROUTINE catalejo_faultable_instruction_outcome_t
catalejo_monitor_amd_arm(CATALEJO_UNUSED const uint8_t *target_address)
{
__asm__ volatile(
"movq %rdi, %rax\n\t"
"xorl %ecx, %ecx\n\t"
"xorl %edx, %edx\n\t"
"1:\n\t"
"monitorx\n\t"
"2:\n\t"
"xorl %eax, %eax\n\t"
"xorl %edx, %edx\n\t"
"ret\n\t"
"3:\n\t"
"movl $" CATALEJO_STR(CATALEJO_OUTCOME_ERROR_VALUE) ", %eax\n\t"
"ret\n\t"
CATALEJO_MONITOR_RECOVERY);
}
CATALEJO_FAULT_ROUTINE catalejo_faultable_instruction_outcome_t catalejo_monitor_amd_wait()
{
__asm__ volatile(
"movq %rbx, %r8\n\t"
"xorl %eax, %eax\n\t"
"movl $0b10, %ecx\n\t"
"movl $" CATALEJO_STR(CATALEJO_MONITOR_WAIT_CYCLES) ", %ebx\n\t"
"1:\n\t"
"mwaitx\n\t"
"2:\n\t"
"movq %r8, %rbx\n\t"
"xorl %eax, %eax\n\t"
"xorl %edx, %edx\n\t"
"ret\n\t"
"3:\n\t"
"movq %r8, %rbx\n\t"
"movl $" CATALEJO_STR(CATALEJO_OUTCOME_ERROR_VALUE) ", %eax\n\t"
"ret\n\t"
CATALEJO_MONITOR_RECOVERY);
}
CATALEJO_FAULT_ROUTINE catalejo_faultable_copy_outcome_t
catalejo_copy(CATALEJO_UNUSED uint8_t *target_address, CATALEJO_UNUSED const uint8_t *target_source,
CATALEJO_UNUSED size_t target_count)
{
__asm__ volatile(
"cld\n\t"
"movq %rdx, %rcx\n\t"
"1:\n\t"
"rep movsb\n\t"
"2:\n\t"
"movq %rcx, %rdx\n\t"
"xorl %eax, %eax\n\t"
"ret\n\t"
"3:\n\t"
"movq %rcx, %rdx\n\t"
"movl $" CATALEJO_STR(CATALEJO_OUTCOME_ERROR_VALUE) ", %eax\n\t"
"ret\n\t"
CATALEJO_ROLLBACK_RECORD("1b", "2b", "3b", CATALEJO_FAULT_EXCEPTION_MEMORY));
}