]> xenbits.xensource.com Git - people/tklengyel/xen.git/commitdiff
Update tsffs.h tsffs
authorTamas K Lengyel <tamas@tklengyel.com>
Sat, 22 Feb 2025 03:06:46 +0000 (22:06 -0500)
committerTamas K Lengyel <tamas@tklengyel.com>
Mon, 24 Feb 2025 00:00:00 +0000 (19:00 -0500)
xen/0001-TSFFS.patch [deleted file]
xen/arch/x86/hvm/vmx/vmx.c
xen/include/xen/sched.h
xen/include/xen/tsffs.h

diff --git a/xen/0001-TSFFS.patch b/xen/0001-TSFFS.patch
deleted file mode 100644 (file)
index 7b637bf..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-From eb65003ff273a7b0488f534ebe184d3bf9f076c0 Mon Sep 17 00:00:00 2001
-From: Tamas K Lengyel <tamas.lengyel@intel.com>
-Date: Fri, 2 Feb 2024 08:27:44 -0500
-Subject: [PATCH] TSFFS
-
----
- xen/arch/x86/domctl.c        |   3 +
- xen/arch/x86/hvm/hypercall.c |  48 +++++++++++--
- xen/arch/x86/hvm/vmx/vmx.c   |   2 +
- xen/common/ubsan/ubsan.c     |   3 +
- xen/drivers/char/console.c   |   5 ++
- xen/include/xen/kfx.h        |  32 +++++++++
- xen/include/xen/sched.h      |   4 ++
- xen/include/xen/tsffs.h      | 131 +++++++++++++++++++++++++++++++++++
- 8 files changed, 223 insertions(+), 5 deletions(-)
- create mode 100644 xen/include/xen/kfx.h
- create mode 100644 xen/include/xen/tsffs.h
-
-diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c
-index 9a72d57333..8b97186331 100644
---- a/xen/arch/x86/domctl.c
-+++ b/xen/arch/x86/domctl.c
-@@ -21,6 +21,9 @@
- #include <xen/iocap.h>
- #include <xen/paging.h>
-+#include <xen/tsffs.h>
-+#include <xen/kfx.h>
-+
- #include <asm/gdbsx.h>
- #include <asm/irq.h>
- #include <asm/hvm/emulate.h>
-diff --git a/xen/arch/x86/hvm/hypercall.c b/xen/arch/x86/hvm/hypercall.c
-index eeb73e1aa5..cdf6a9d61f 100644
---- a/xen/arch/x86/hvm/hypercall.c
-+++ b/xen/arch/x86/hvm/hypercall.c
-@@ -11,6 +11,8 @@
- #include <xen/ioreq.h>
- #include <xen/nospec.h>
-+#include <xen/tsffs.h>
-+
- #include <asm/hvm/emulate.h>
- #include <asm/hvm/support.h>
- #include <asm/hvm/viridian.h>
-@@ -104,8 +106,40 @@ int hvm_hypercall(struct cpu_user_regs *regs)
-     struct vcpu *curr = current;
-     struct domain *currd = curr->domain;
-     int mode = hvm_guest_x86_mode(curr);
--    unsigned long eax = regs->eax;
-+    unsigned long eax;
-     unsigned int token;
-+    int ret;
-+
-+    struct cpu_user_regs fme = *regs;
-+    size_t _size = sizeof(struct cpu_user_regs);
-+    HARNESS_START(&fme, &_size);
-+
-+    regs->r8 = fme.r8;
-+    regs->r9 = fme.r9;
-+    regs->r10 = fme.r10;
-+    regs->r11 = fme.r11;
-+    regs->r12 = fme.r12;
-+    regs->r13 = fme.r13;
-+    regs->r14 = fme.r14;
-+    regs->r15 = fme.r15;
-+    regs->rax = fme.rax;
-+    regs->rbx = fme.rbx;
-+    regs->rcx = fme.rcx;
-+    regs->rdx = fme.rdx;
-+    regs->rsi = fme.rsi;
-+    regs->rdi = fme.rdi;
-+    regs->rip = fme.rip;
-+    regs->rsp = fme.rsp;
-+    regs->rbp = fme.rbp;
-+    regs->rflags = fme.rflags;
-+    regs->cs = fme.cs;
-+    regs->ss = fme.ss;
-+    regs->es = fme.es;
-+    regs->ds = fme.ds;
-+    regs->fs = fme.fs;
-+    regs->gs = fme.gs;
-+
-+    eax = regs->eax;
-     switch ( mode )
-     {
-@@ -123,7 +157,8 @@ int hvm_hypercall(struct cpu_user_regs *regs)
-         {
-     default:
-             regs->rax = -EPERM;
--            return HVM_HCALL_completed;
-+            ret = HVM_HCALL_completed;
-+            goto out;
-         }
-     case 0:
-         break;
-@@ -131,8 +166,6 @@ int hvm_hypercall(struct cpu_user_regs *regs)
-     if ( (eax & 0x80000000U) && is_viridian_domain(currd) )
-     {
--        int ret;
--
-         /* See comment below. */
-         token = hvmemul_cache_disable(curr);
-@@ -140,7 +173,7 @@ int hvm_hypercall(struct cpu_user_regs *regs)
-         hvmemul_cache_restore(curr, token);
--        return ret;
-+        goto out;
-     }
-     /*
-@@ -188,7 +221,12 @@ int hvm_hypercall(struct cpu_user_regs *regs)
-     perfc_incra(hypercalls, eax);
-+    HARNESS_STOP();
-     return curr->hcall_preempted ? HVM_HCALL_preempted : HVM_HCALL_completed;
-+
-+    out:
-+        HARNESS_STOP();
-+        return ret;
- }
- enum mc_disposition hvm_do_multicall_call(struct mc_state *state)
-diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
-index 1500dca603..9e1230ee45 100644
---- a/xen/arch/x86/hvm/vmx/vmx.c
-+++ b/xen/arch/x86/hvm/vmx/vmx.c
-@@ -4780,6 +4780,8 @@ bool asmlinkage vmx_vmenter_helper(const struct cpu_user_regs *regs)
-     struct hvm_vcpu_asid *p_asid;
-     bool need_flush;
-+    HARNESS_STOP();
-+
-     ASSERT(hvmemul_cache_disabled(curr));
-     /* Shadow EPTP can't be updated here because irqs are disabled */
-diff --git a/xen/common/ubsan/ubsan.c b/xen/common/ubsan/ubsan.c
-index 5d50c10742..6f734aa473 100644
---- a/xen/common/ubsan/ubsan.c
-+++ b/xen/common/ubsan/ubsan.c
-@@ -13,6 +13,9 @@
- #include <xen/spinlock.h>
- #include <xen/percpu.h>
-+#include <xen/tsffs.h>
-+#include <xen/kfx.h>
-+
- #define __noreturn    noreturn
- #define pr_err(...) printk(XENLOG_ERR __VA_ARGS__)
- struct xen_ubsan { int in_ubsan; };
-diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
-index dce0226e87..d7a2751bcd 100644
---- a/xen/drivers/char/console.c
-+++ b/xen/drivers/char/console.c
-@@ -33,6 +33,9 @@
- #include <xen/pv_console.h>
- #include <asm/setup.h>
-+#include <xen/tsffs.h>
-+#include <xen/kfx.h>
-+
- #ifdef CONFIG_X86
- #include <xen/consoled.h>
- #include <asm/guest.h>
-@@ -1269,6 +1272,8 @@ void panic(const char *fmt, ...)
-     static DEFINE_SPINLOCK(lock);
-     static char buf[128];
-+    HARNESS_ASSERT();
-+
-     spin_debug_disable();
-     spinlock_profile_printall('\0');
-     debugtrace_dump();
-diff --git a/xen/include/xen/kfx.h b/xen/include/xen/kfx.h
-new file mode 100644
-index 0000000000..afde305796
---- /dev/null
-+++ b/xen/include/xen/kfx.h
-@@ -0,0 +1,32 @@
-+#ifndef KFX_H
-+#define KFX_H
-+
-+static inline void harness_extended(unsigned int magic, void *a, size_t s)
-+{
-+    asm volatile ("cpuid"
-+                  : "=a" (magic), "=c" (magic), "=S" (magic)
-+                  : "a" (magic), "c" (s), "S" (a)
-+                  : "bx", "dx");
-+}
-+
-+static inline void harness(void)
-+{
-+    unsigned int tmp;
-+
-+    asm volatile ("cpuid"
-+                  : "=a" (tmp)
-+                  : "a" (0x13371337)
-+                  : "bx", "cx", "dx");
-+}
-+
-+static inline void kfx_sink(void)
-+{
-+    unsigned int tmp;
-+
-+    asm volatile ("int3"
-+                  : "=a" (tmp)
-+                  : "a" (0x13371337)
-+                  : "bx", "cx", "dx");
-+}
-+
-+#endif
-diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
-index 9da91e0e62..8f04f6110f 100644
---- a/xen/include/xen/sched.h
-+++ b/xen/include/xen/sched.h
-@@ -24,6 +24,7 @@
- #include <asm/current.h>
- #include <xen/vpci.h>
- #include <xen/wait.h>
-+#include <xen/tsffs.h>
- #include <public/xen.h>
- #include <public/domctl.h>
- #include <public/sysctl.h>
-@@ -792,6 +793,9 @@ void vcpu_end_shutdown_deferral(struct vcpu *v);
- void __domain_crash(struct domain *d);
- #define domain_crash(d, ...)                                        \
-     do {                                                            \
-+        HARNESS_STOP();                                             \
-+        __arch_harness_stop(MAGIC_ALT_0);                           \
-+        __arch_harness_stop(MAGIC_ALT_1);                           \
-         if ( count_args(__VA_ARGS__) == 0 )                         \
-             printk(XENLOG_ERR "domain_crash called from %s:%d\n",   \
-                    __FILE__, __LINE__);                             \
-diff --git a/xen/include/xen/tsffs.h b/xen/include/xen/tsffs.h
-new file mode 100644
-index 0000000000..c2afe7ace3
---- /dev/null
-+++ b/xen/include/xen/tsffs.h
-@@ -0,0 +1,131 @@
-+/// Definitions and macros for compiled-in harnessing of C and C++ target
-+/// software for the RISC-V (64-bit) architecture
-+///
-+/// Example:
-+/// ```c
-+/// #include "tsffs-gcc-x86_64.h"
-+///
-+/// int main() {
-+///    char buf[0x10];
-+///    size_t size = 0x10;
-+///    size_t *size_ptr = &size;
-+///    HARNESS_START((char *)buf, size_ptr);
-+///    int retval = YourSpecialDecoder(buf, *size_ptr);
-+///    if (!retval) {
-+///        HARNESS_ASSERT();
-+///    } else {
-+///        HARNESS_STOP();
-+///    }
-+/// }
-+/// ```
-+
-+#ifndef TSFFS_H
-+#define TSFFS_H
-+
-+/// Define common with LibFuzzer and other fuzzers to allow code that is
-+/// fuzzing-specific to be left in the codebase. See
-+/// https://llvm.org/docs/LibFuzzer.html#id35 for more information
-+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-+#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1)
-+#endif  // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-+
-+/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction
-+/// that is treated as a magic instruction.
-+#define MAGIC (0x4711U)
-+/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the
-+/// fuzzing loop
-+#define MAGIC_START (0x0001U)
-+/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and
-+/// reset the fuzzing loop
-+#define MAGIC_STOP (0x0002U)
-+/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error
-+/// has occurred and the testcase input should be saved as a solution
-+#define MAGIC_ASSERT (0x0003U)
-+
-+/// Alternative magic numbers that can be used for start and stop events in
-+/// conjunction with setting the magic number for each event via the SIMICS or
-+/// SIMICS Python script interface
-+#define MAGIC_ALT_0 (0x0004U)
-+#define MAGIC_ALT_1 (0x0005U)
-+#define MAGIC_ALT_2 (0x0006U)
-+#define MAGIC_ALT_3 (0x0007U)
-+#define MAGIC_ALT_4 (0x0008U)
-+#define MAGIC_ALT_5 (0x0009U)
-+#define MAGIC_ALT_6 (0x000aU)
-+#define MAGIC_ALT_7 (0x000bU)
-+
-+/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture
-+/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the
-+/// value of the pointer to the testcase and register `rsi` to the value of the
-+/// pointer to the testcase size. These registers are accessed by the fuzzer and
-+/// are defined per-architecture.
-+#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1)              \
-+  unsigned int _a __attribute__((unused)) = 0;                          \
-+  __asm__ __volatile__("cpuid"                                          \
-+                       : "=a"(_a)                                       \
-+                       : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \
-+                       : "rbx", "rcx", "rdx");
-+
-+/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture
-+/// (`cpuid`) with a specific value of `n`
-+#define __cpuid(value)                         \
-+  unsigned int _a __attribute__((unused)) = 0; \
-+  __asm__ __volatile__("cpuid\n\t"             \
-+                       : "=a"(_a)              \
-+                       : "a"(value)            \
-+                       : "rbx", "rcx", "rdx")
-+
-+/// Signal the fuzzer using a specific magic value `start` to start the fuzzing
-+/// loop at the point this macro is called. A snapshot will be taken when the
-+/// macro is called, and the maximum testcase size `*size_ptr` will be saved as
-+/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input
-+/// (the "testcase") will be written to `*testcase_ptr` as if running
-+/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual
-+/// size of the current testcase will be written to
-+/// `*size_ptr` as if running `*size_ptr = current_testcase_size`.
-+#define __arch_harness_start(start, testcase_ptr, size_ptr) \
-+  do {                                                      \
-+    unsigned int magic = (start << 0x10U) | MAGIC;          \
-+    __cpuid_extended2(magic, testcase_ptr, size_ptr);       \
-+  } while (0)
-+
-+/// Signal the fuzzer using the a specific magic value (`stop`) to stop and
-+/// reset to the beginning of the fuzzing loop with a "normal" stop status,
-+/// indicating no solution has occurred.
-+#define __arch_harness_stop(stop)                 \
-+  do {                                            \
-+    unsigned int magic = (stop << 0x10U) | MAGIC; \
-+    __cpuid(magic);                               \
-+  } while (0)
-+
-+/// Signal the fuzzer using the default magic value to start the fuzzing loop at
-+/// the point this macro is called. A snapshot will be taken when the macro is
-+/// called, and the maximum testcase size `*size_ptr` will be saved as
-+/// `max_testcase_size`.  Each iteration of the fuzzing loop, the fuzzer input
-+/// (the "testcase") will be written to
-+/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase,
-+/// max_testcase_size)`, and the actual size of the current testcase will be
-+/// written to
-+/// `*size_ptr` as if running `*size_ptr = current_testcase_size`.
-+#define HARNESS_START(testcase_ptr, size_ptr)                  \
-+  do {                                                         \
-+    __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \
-+  } while (0)
-+
-+/// Signal the fuzzer using the default magic value to stop and reset to the
-+/// beginning of the fuzzing loop with a "normal" stop status, indicating no
-+/// solution has occurred.
-+#define HARNESS_STOP()               \
-+  do {                               \
-+    __arch_harness_stop(MAGIC_STOP); \
-+  } while (0)
-+
-+/// Signal the fuzzer using the default magic value to stop and reset to the
-+/// beginning of the fuzzing loop with a "solution" stop status, indicating some
-+/// custom error has occurred.
-+#define HARNESS_ASSERT()               \
-+  do {                                 \
-+    __arch_harness_stop(MAGIC_ASSERT); \
-+  } while (0)
-+
-+#endif  // TSFFS_H
-\ No newline at end of file
--- 
-2.34.1
-
index d6effaefc79de14d0749b29a69ab95ea3d7660fe..34aaefa2bf9e5723d678f7bf514ee92dc110d0c7 100644 (file)
@@ -4073,9 +4073,8 @@ void asmlinkage vmx_vmexit_handler(struct cpu_user_regs *regs)
     __vmread(GUEST_RSP,    &regs->rsp);
     __vmread(GUEST_RFLAGS, &regs->rflags);
 
-    tsffs_size = sizeof(struct tsffs_fme);
     memcpy(&tsffs_fme.regs, regs, sizeof(struct cpu_user_regs));
-    HARNESS_START(&tsffs_fme, &tsffs_size);
+    HARNESS_START_WITH_MAXIMUM_SIZE(&tsffs_fme, sizeof(struct tsffs_fme));
 
     regs->r8 = tsffs_fme.regs.r8;
     regs->r9 = tsffs_fme.regs.r9;
index 4341e55c7eff13a00135e25ec1ea309ddd58e538..8931e15159f074c7c0a200db0020fd86d6b3b1ba 100644 (file)
@@ -818,8 +818,6 @@ void __domain_crash(struct domain *d);
 #define domain_crash(d, ...)                                        \
     do {                                                            \
         HARNESS_STOP();                                             \
-        __arch_harness_stop(MAGIC_ALT_0);                           \
-        __arch_harness_stop(MAGIC_ALT_1);                           \
         if ( count_args(__VA_ARGS__) == 0 )                         \
             printk(XENLOG_ERR "domain_crash called from %s:%d\n",   \
                    __FILE__, __LINE__);                             \
index 874b6292007c36582978db174cca2c139e5d8442..865adc2c941f9bf2fa0441c114621b0ddd5d7afe 100644 (file)
@@ -1,23 +1,8 @@
+// Copyright (C) 2024 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
 /// Definitions and macros for compiled-in harnessing of C and C++ target
-/// software for the RISC-V (64-bit) architecture
-///
-/// Example:
-/// ```c
-/// #include "tsffs-gcc-x86_64.h"
-///
-/// int main() {
-///    char buf[0x10];
-///    size_t size = 0x10;
-///    size_t *size_ptr = &size;
-///    HARNESS_START((char *)buf, size_ptr);
-///    int retval = YourSpecialDecoder(buf, *size_ptr);
-///    if (!retval) {
-///        HARNESS_ASSERT();
-///    } else {
-///        HARNESS_STOP();
-///    }
-/// }
-/// ```
+/// software for the x86_64 architecture.
 
 #ifndef TSFFS_H
 #define TSFFS_H
 #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1)
 #endif  // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
 
+/// __cpuid
+///
+/// Invoke the CPUID instruction with a specific value `value` in register
+/// `rax`.
+///
+/// # Arguments
+///
+/// - `value`: The value to load into the `rax` register before invoking the
+///   CPUID instruction
+#define __cpuid(value)                                          \
+  unsigned int _a __attribute__((unused)) = 0;                  \
+  unsigned int _b __attribute__((unused)) = 0;                  \
+  unsigned int _c __attribute__((unused)) = 0;                  \
+  unsigned int _d __attribute__((unused)) = 0;                  \
+  __asm__ __volatile__("cpuid\n\t"                              \
+                       : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \
+                       : "a"(value));
+
+/// __cpuid_extended1
+///
+/// Invoke the CPUID instruction with a specific value `value` in register
+/// `rax` and a pseudo-argument in register `rdi`.
+///
+/// # Arguments
+///
+/// - `value`: The value to load into the `rax` register before invoking the
+///   CPUID instruction
+/// - `arg0`: The value to load into the `rdi` register before invoking the
+///   CPUID instruction
+#define __cpuid_extended1(value, arg0)                          \
+  unsigned int _a __attribute__((unused)) = 0;                  \
+  unsigned int _b __attribute__((unused)) = 0;                  \
+  unsigned int _c __attribute__((unused)) = 0;                  \
+  unsigned int _d __attribute__((unused)) = 0;                  \
+  __asm__ __volatile__("cpuid\n\t"                              \
+                       : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \
+                       : "a"(value), "D"(arg0));
+
+/// __cpuid_extended2
+///
+/// Invoke the CPUID instruction with a specific value `value` in register
+/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`.
+///
+/// # Arguments
+///
+/// - `value`: The value to load into the `rax` register before invoking the
+///   CPUID instruction
+/// - `arg0`: The value to load into the `rdi` register before invoking the
+///   CPUID instruction
+/// - `arg1`: The value to load into the `rsi` register before invoking the
+///   CPUID instruction
+#define __cpuid_extended2(value, arg0, arg1)                    \
+  unsigned int _a __attribute__((unused)) = 0;                  \
+  unsigned int _b __attribute__((unused)) = 0;                  \
+  unsigned int _c __attribute__((unused)) = 0;                  \
+  unsigned int _d __attribute__((unused)) = 0;                  \
+  __asm__ __volatile__("cpuid\n\t"                              \
+                       : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \
+                       : "a"(value), "D"(arg0), "S"(arg1));
+
+/// __cpuid_extended3
+///
+/// Invoke the CPUID instruction with a specific value `value` in register
+/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`.
+///
+/// # Arguments
+///
+/// - `value`: The value to load into the `rax` register before invoking the
+///   CPUID instruction
+/// - `arg0`: The value to load into the `rdi` register before invoking the
+///   CPUID instruction
+/// - `arg1`: The value to load into the `rsi` register before invoking the
+///   CPUID instruction
+/// - `arg2`: The value to load into the `rdx` register before invoking the
+///   CPUID instruction
+#define __cpuid_extended3(value, arg0, arg1, arg2)              \
+  unsigned int _a __attribute__((unused)) = 0;                  \
+  unsigned int _b __attribute__((unused)) = 0;                  \
+  unsigned int _c __attribute__((unused)) = 0;                  \
+  unsigned int _d __attribute__((unused)) = 0;                  \
+  __asm__ __volatile__("cpuid\n\t"                              \
+                       : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \
+                       : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2));
+
+/// __cpuid_extended4
+///
+/// Invoke the CPUID instruction with a specific value `value` in register
+/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`.
+///
+/// # Arguments
+///
+/// - `value`: The value to load into the `rax` register before invoking the
+///   CPUID instruction
+/// - `arg0`: The value to load into the `rdi` register before invoking the
+///   CPUID instruction
+/// - `arg1`: The value to load into the `rsi` register before invoking the
+///   CPUID instruction
+/// - `arg2`: The value to load into the `rdx` register before invoking the
+///   CPUID instruction
+/// - `arg3`: The value to load into the `rcx` register before invoking the
+///   CPUID instruction
+#define __cpuid_extended4(value, arg0, arg1, arg2, arg3)              \
+  unsigned int _a __attribute__((unused)) = 0;                        \
+  unsigned int _b __attribute__((unused)) = 0;                        \
+  unsigned int _c __attribute__((unused)) = 0;                        \
+  unsigned int _d __attribute__((unused)) = 0;                        \
+  __asm__ __volatile__("cpuid\n\t"                                    \
+                       : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d)       \
+                       : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \
+                         "c"(arg3));
+
 /// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction
 /// that is treated as a magic instruction.
 #define MAGIC (0x4711U)
-/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the
-/// fuzzing loop
-#define MAGIC_START (0x0001U)
-/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and
-/// reset the fuzzing loop
-#define MAGIC_STOP (0x0002U)
-/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error
-/// has occurred and the testcase input should be saved as a solution
-#define MAGIC_ASSERT (0x0003U)
-
-/// Alternative magic numbers that can be used for start and stop events in
-/// conjunction with setting the magic number for each event via the SIMICS or
-/// SIMICS Python script interface
-#define MAGIC_ALT_0 (0x0004U)
-#define MAGIC_ALT_1 (0x0005U)
-#define MAGIC_ALT_2 (0x0006U)
-#define MAGIC_ALT_3 (0x0007U)
-#define MAGIC_ALT_4 (0x0008U)
-#define MAGIC_ALT_5 (0x0009U)
-#define MAGIC_ALT_6 (0x000aU)
-#define MAGIC_ALT_7 (0x000bU)
-
-/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture
-/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the
-/// value of the pointer to the testcase and register `rsi` to the value of the
-/// pointer to the testcase size. These registers are accessed by the fuzzer and
-/// are defined per-architecture.
-#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1)              \
-  unsigned int _a __attribute__((unused)) = 0;                          \
-  __asm__ __volatile__("cpuid"                                          \
-                       : "=a"(_a)                                       \
-                       : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \
-                       : "rbx", "rcx", "rdx");
-
-/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture
-/// (`cpuid`) with a specific value of `n`
-#define __cpuid(value)                         \
-  unsigned int _a __attribute__((unused)) = 0; \
-  __asm__ __volatile__("cpuid\n\t"             \
-                       : "=a"(_a)              \
-                       : "a"(value)            \
-                       : "rbx", "rcx", "rdx")
-
-/// Signal the fuzzer using a specific magic value `start` to start the fuzzing
-/// loop at the point this macro is called. A snapshot will be taken when the
-/// macro is called, and the maximum testcase size `*size_ptr` will be saved as
-/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input
-/// (the "testcase") will be written to `*testcase_ptr` as if running
-/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual
-/// size of the current testcase will be written to
-/// `*size_ptr` as if running `*size_ptr = current_testcase_size`.
-#define __arch_harness_start(start, testcase_ptr, size_ptr) \
-  do {                                                      \
-    unsigned int magic = (start << 0x10U) | MAGIC;          \
-    __cpuid_extended2(magic, testcase_ptr, size_ptr);       \
-  } while (0)
-
-/// Signal the fuzzer using the a specific magic value (`stop`) to stop and
-/// reset to the beginning of the fuzzing loop with a "normal" stop status,
-/// indicating no solution has occurred.
-#define __arch_harness_stop(stop)                 \
-  do {                                            \
-    unsigned int magic = (stop << 0x10U) | MAGIC; \
-    __cpuid(magic);                               \
-  } while (0)
-
-/// Signal the fuzzer using the default magic value to start the fuzzing loop at
-/// the point this macro is called. A snapshot will be taken when the macro is
-/// called, and the maximum testcase size `*size_ptr` will be saved as
-/// `max_testcase_size`.  Each iteration of the fuzzing loop, the fuzzer input
-/// (the "testcase") will be written to
-/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase,
-/// max_testcase_size)`, and the actual size of the current testcase will be
-/// written to
-/// `*size_ptr` as if running `*size_ptr = current_testcase_size`.
-#define HARNESS_START(testcase_ptr, size_ptr)                  \
-  do {                                                         \
-    __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \
-  } while (0)
-
-/// Signal the fuzzer using the default magic value to stop and reset to the
-/// beginning of the fuzzing loop with a "normal" stop status, indicating no
-/// solution has occurred.
-#define HARNESS_STOP()               \
-  do {                               \
-    __arch_harness_stop(MAGIC_STOP); \
-  } while (0)
-
-/// Signal the fuzzer using the default magic value to stop and reset to the
-/// beginning of the fuzzing loop with a "solution" stop status, indicating some
-/// custom error has occurred.
-#define HARNESS_ASSERT()               \
-  do {                                 \
-    __arch_harness_stop(MAGIC_ASSERT); \
-  } while (0)
+
+/// The default index number used for magic instructions. All magic instructions
+/// support multiple start and stop indices, which defaults to 0 if not
+/// specified.
+#define DEFAULT_INDEX (0x0000U)
+
+/// Pseudo-hypercall number to signal the fuzzer to use the first argument to
+/// the magic instruction as the pointer to the testcase buffer and the second
+/// argument as a pointer to the size of the testcase buffer.
+#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U)
+
+/// HARNESS_START
+///
+/// Signal the fuzzer to start the fuzzing loop at the point this macro is
+/// called. The default "index" of 0 will be used. If you need multiple start
+/// harnesses compiled into the same binary, you can use the
+/// `HARNESS_START_INDEX` macro to specify different indices, then enable them
+/// at runtime by configuring the fuzzer.
+///
+/// When this macro is called:
+///
+/// - A snapshot will be taken and saved
+/// - The buffer pointed to by `buffer` will be saved and used as the testcase
+///   buffer. Each
+///   fuzzing iteration, a new test case will be written to this buffer.
+/// - The size of the buffer pointed to by `size_ptr` will be saved as the
+///   maximum testcase size. Each fuzzing iteration, the actual size of the
+///   current testcase will be written to `*size_ptr`.
+///
+/// # Arguments
+///
+/// - `buffer`: The pointer to the testcase buffer
+/// - `size_ptr`: The pointer to the size of the testcase buffer
+///
+/// # Example
+///
+/// ```
+/// unsigned char buffer[1024];
+/// size_t size;
+/// HARNESS_START(buffer, &size);
+/// ```
+#define HARNESS_START(buffer, size_ptr)                                  \
+  do {                                                                   \
+    unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \
+    __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr);           \
+  } while (0);
+
+/// HARNESS_START_INDEX
+///
+/// Signal the fuzzer to start the fuzzing loop at the point this macro is
+/// called. The index specified by `start_index` will be used. If you need
+/// multiple start harnesses compiled into the same binary, you can use this
+/// macro to specify different indices, then enable them at runtime by
+/// configuring the fuzzer.
+///
+/// When this macro is called:
+///
+/// - A snapshot will be taken and saved
+/// - The buffer pointed to by `buffer` will be saved and used as the testcase
+///   buffer. Each
+///   fuzzing iteration, a new test case will be written to this buffer.
+/// - The size of the buffer pointed to by `size_ptr` will be saved as the
+///   maximum testcase size. Each fuzzing iteration, the actual size of the
+///   current testcase will be written to `*size_ptr`.
+///
+/// # Arguments
+///
+/// - `start_index`: The index to use for this start harness
+/// - `buffer`: The pointer to the testcase buffer
+/// - `size_ptr`: The pointer to the size of the testcase buffer
+///
+/// # Example
+///
+/// ```
+/// unsigned char buffer[1024];
+/// size_t size;
+/// HARNESS_START_INDEX(0x0001U, buffer, &size);
+/// ```
+#define HARNESS_START_INDEX(start_index, buffer, size_ptr)               \
+  do {                                                                   \
+    unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \
+    __cpuid_extended3(value, start_index, buffer, size_ptr);             \
+  } while (0);
+
+/// Pseudo-hypercall number to signal the fuzzer to use the first argument to
+/// the magic instruction as the pointer to the testcase buffer and the second
+/// argument as the maximum size of the testcase buffer.
+#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U)
+
+/// HARNESS_START_WITH_MAXIMUM_SIZE
+///
+/// Signal the fuzzer to start the fuzzing loop at the point this macro is
+/// called. The default "index" of 0 will be used. If you need multiple start
+/// harnesses compiled into the same binary, you can use the
+/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices,
+/// then enable them at runtime by configuring the fuzzer.
+///
+/// When this macro is called:
+///
+/// - A snapshot will be taken and saved
+/// - The buffer pointed to by `buffer` will be saved and used as the testcase
+///   buffer. Each
+///   fuzzing iteration, a new test case will be written to this buffer.
+/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing
+///   test cases will be truncated to this size before being written to the
+///   buffer.
+///
+/// # Arguments
+///
+/// - `buffer`: The pointer to the testcase buffer
+/// - `max_size`: The maximum size of the testcase buffer
+///
+/// # Example
+///
+/// ```
+/// unsigned char buffer[1024];
+/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024);
+/// ```
+#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size)                \
+  do {                                                                   \
+    unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \
+    __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size);           \
+  } while (0);
+
+/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX
+///
+/// Signal the fuzzer to start the fuzzing loop at the point this macro is
+/// called. The index specified by `start_index` will be used. If you need
+/// multiple start harnesses compiled into the same binary, you can use this
+/// macro to specify different indices, then enable them at runtime by
+/// configuring the fuzzer.
+///
+/// When this macro is called:
+///
+/// - A snapshot will be taken and saved
+/// - The buffer pointed to by `buffer` will be saved and used as the testcase
+///   buffer. Each
+///   fuzzing iteration, a new test case will be written to this buffer.
+/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing
+///   test cases will be truncated to this size before being written to the
+///   buffer.
+///
+/// # Arguments
+///
+/// - `start_index`: The index to use for this start harness
+/// - `buffer`: The pointer to the testcase buffer
+/// - `max_size`: The maximum size of the testcase buffer
+///
+/// # Example
+///
+/// ```
+/// unsigned char buffer[1024];
+/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024);
+/// ```
+#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \
+  do {                                                                       \
+    unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC;     \
+    __cpuid_extended3(value, start_index, buffer, max_size);                 \
+  } while (0);
+
+/// Pseudo-hypercall number to signal the fuzzer to use the first argument to
+/// the magic instruction as the pointer to the testcase buffer, the second
+/// argument as a pointer to the size of the testcase buffer, and the third
+/// argument as the maximum size of the testcase buffer.
+#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U)
+
+/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR
+///
+/// Signal the fuzzer to start the fuzzing loop at the point this macro is
+/// called. The default "index" of 0 will be used. If you need multiple start
+/// harnesses compiled into the same binary, you can use the
+/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different
+/// indices, then enable them at runtime by configuring the fuzzer.
+///
+/// When this macro is called:
+///
+/// - A snapshot will be taken and saved
+/// - The buffer pointed to by `buffer` will be saved and used as the testcase
+///   buffer. Each
+///   fuzzing iteration, a new test case will be written to this buffer.
+/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual
+/// size of the current testcase will be written to `*size_ptr`.
+/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing
+///   test cases will be truncated to this size before being written to the
+///   buffer.
+///
+/// # Arguments
+///
+/// - `buffer`: The pointer to the testcase buffer
+/// - `size_ptr`: The pointer to the size of the testcase buffer
+/// - `max_size`: The maximum size of the testcase buffer
+///
+/// # Example
+///
+/// ```
+/// unsigned char buffer[1024];
+/// size_t size;
+/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024);
+/// ```
+#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size)  \
+  do {                                                                       \
+    unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \
+    __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size);     \
+  } while (0);
+
+/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX
+///
+/// Signal the fuzzer to start the fuzzing loop at the point this macro is
+/// called. The index specified by `start_index` will be used. If you need
+/// multiple start harnesses compiled into the same binary, you can use this
+/// macro to specify different indices, then enable them at runtime by
+/// configuring the fuzzer.
+///
+/// When this macro is called:
+///
+/// - A snapshot will be taken and saved
+/// - The buffer pointed to by `buffer` will be saved and used as the testcase
+///   buffer. Each
+///   fuzzing iteration, a new test case will be written to this buffer.
+/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual
+/// size of the current testcase will be written to `*size_ptr`.
+/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing
+///   test cases will be truncated to this size before being written to the
+///   buffer.
+///
+/// # Arguments
+///
+/// - `start_index`: The index to use for this start harness
+/// - `buffer`: The pointer to the testcase buffer
+/// - `size_ptr`: The pointer to the size of the testcase buffer
+/// - `max_size`: The maximum size of the testcase buffer
+///
+/// # Example
+///
+/// ```
+/// unsigned char buffer[1024];
+/// size_t size;
+/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024);
+/// ```
+#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer,   \
+                                                      size_ptr, max_size)    \
+  do {                                                                       \
+    unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \
+    __cpuid_extended4(value, start_index, buffer, size_ptr, max_size);       \
+  } while (0);
+
+/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing
+/// iteration and reset to the beginning of the fuzzing loop with a "normal"
+/// stop status, indicating no solution has occurred.
+#define N_STOP_NORMAL (0x0004U)
+
+/// HARNESS_STOP
+///
+/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop
+/// with a "normal" stop status, indicating no solution has occurred. The
+/// default index of 0 will be used. If you need to differentiate between
+/// multiple stop harnesses compiled into the same binary, you can use the
+/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at
+/// runtime by configuring the fuzzer.
+///
+/// # Example
+///
+/// ```
+/// HARNESS_STOP();
+/// ```
+#define HARNESS_STOP()                                     \
+  do {                                                     \
+    unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \
+    __cpuid_extended1(value, DEFAULT_INDEX);               \
+  } while (0);
+
+/// HARNESS_STOP_INDEX
+///
+/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop
+/// with a "normal" stop status, indicating no solution has occurred. The index
+/// specified by `stop_index` will be used. If you need to differentiate between
+/// multiple stop harnesses compiled into the same binary, you can use this
+/// macro to specify different indices, then enable them at runtime by
+/// configuring the fuzzer.
+///
+/// # Arguments
+///
+/// - `stop_index`: The index to use for this stop harness
+///
+/// # Example
+///
+/// ```
+/// HARNESS_STOP_INDEX(0x0001U);
+/// ```
+#define HARNESS_STOP_INDEX(stop_index)                     \
+  do {                                                     \
+    unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \
+    __cpuid_extended1(value, stop_index);                  \
+  } while (0);
+
+/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has
+/// occurred, and the fuzzer should stop the current fuzzing iteration and reset
+/// to the beginning of the fuzzing loop with a "solution" stop status.
+#define N_STOP_ASSERT (0x0005U)
+
+/// HARNESS_ASSERT
+///
+/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer
+/// should stop the current fuzzing iteration and reset to the beginning of the
+/// fuzzing loop with a "solution" stop status. The default index of 0 will be
+/// used. If you need to differentiate between multiple assertion harnesses
+/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro
+/// to specify different indices, then enable them at runtime by configuring the
+/// fuzzer.
+///
+/// # Example
+///
+/// ```
+/// HARNESS_ASSERT();
+/// ```
+#define HARNESS_ASSERT()                                   \
+  do {                                                     \
+    unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \
+    __cpuid_extended1(value, DEFAULT_INDEX);               \
+  } while (0);
+
+/// HARNESS_ASSERT_INDEX
+///
+/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer
+/// should stop the current fuzzing iteration and reset to the beginning of the
+/// fuzzing loop with a "solution" stop status. The index specified by
+/// `assert_index` will be used. If you need to differentiate between multiple
+/// assertion harnesses compiled into the same binary, you can use this macro to
+/// specify different indices, then enable them at runtime by configuring the
+/// fuzzer.
+///
+/// # Arguments
+///
+/// - `assert_index`: The index to use for this assertion harness
+///
+/// # Example
+///
+/// ```
+/// HARNESS_ASSERT_INDEX(0x0001U);
+/// ```
+#define HARNESS_ASSERT_INDEX(assert_index)                 \
+  do {                                                     \
+    unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \
+    __cpuid_extended1(value, assert_index);                \
+  } while (0);
 
 struct tsffs_fme {
     struct cpu_user_regs regs;
@@ -138,5 +484,4 @@ static size_t tsffs_size;
 static uint8_t tsffs_vmread_counter;
 static struct tsffs_fme tsffs_fme;
 
-
 #endif  // TSFFS_H