Skip to content

Commit

Permalink
executor/x86: [feat] add a state machine to monitor measurement status
Browse files Browse the repository at this point in the history
  • Loading branch information
OleksiiOleksenko committed Jul 24, 2024
1 parent 1539a2b commit 126cdeb
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 91 deletions.
78 changes: 78 additions & 0 deletions docs/registers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Register Allocation

The test cases are executed in a sandboxed environment, some of the registers are reserved for internal use, and some are available for use in the test cases.
Below is a list of registers and their purpose.

## `R15`

Contains the base address of the UTILITY area in the [sandbox](./docs/sandbox.md).

If the test case does not enter a VM, the register value remains constant during the execution of the test cases.
Otherwise, the register value is updated to point to the UTILITY area of the currently active VM when the `switch_h2g` macro is called, and it is restored to the original value when the `switch_g2h` macro is called.

The register is used by internal functions, such as the implementation of Prime+Probe.

## `R14`

Contains the base address of the current actor's [sandbox](./docs/sandbox.md) (namely, it points to the base of the actor's MAIN area).

At the beginning of the test case execution, the register is set to the base address of the MAIN area of the first actor (actor `main`). The register value is updated to point to the MAIN area of the currently active actor when a macro from the `landing_*` group of macros is called. It is also updated by the `fault_handler` macro.

The register is used in test cases as a part of the sandboxing mechanism.
For example, all generated memory accesses are relative to the value stored in `R14`, and have the form of `[R14 + offset]`.


## `R13` (`HTRACE_REGISTER` constant in the kernel module)

Contains either intermediate or final result of the hardware trace measurements.

Before entering the test case, the register is set to 0.
When a `measurement_start` macro is executed, the register is (optionally) set to the starting value,
such a initial reading of time stamp counter when the `TSC` mode is used.
When a `measurement_end` macro is executed, the register is updated with the final value of the measurement and contains the resulting hardware trace.

## `R12` (`STATUS_REGISTER` constant in the kernel module)

Contains a compressed status of the test case execution:

Bits[0:7] contain a measurement status.
At the beginning of the test case execution, the bits are set to 0.
When `measurement_start` macro is executed, the bits are set to 1.
When `measurement_end` macro is executed, the bits are set to 2.
If the measurement status is not 2 at the end of the test case execution, the kernel module will report an error.

Bits[8:31] are unused.

Bits[32:63] contain a counter of SMI (System Management Interrupt) events.
The counter is set automatically before entering the test case (`READ_SMI_START`), and updated when the test case finishes (`READ_SMI_END`).
If the difference between the readings is not 0, the kernel module will report an error.

## `R11`

The register is used as a temporary buffer by some of the macros.

Before entering the test case, the register is set to 0.
When certain macros are executed (e.g., `set_k2u_target`), the register will contain temporary values.
The register should not be used in the test case, as the temporary value may be consumed by latter macros.

## `R10, R9, R8`

Stores the values of performance counters.
`R10` stores the value of performance counter #1, `R9` stores the value of performance counter #2, and `R8` stores the value of performance counter #3.

Before entering the test case, the registers are set to 0.
When a `measurement_start` macro is executed, the registers are (optionally) set to the starting values.
When a `measurement_end` macro is executed, the registers are updated with the final values of the measurements.


## Other General Purpose Registers

The remaining registers (`rax`, `rcx`, `rdx`, `rsi`, `rdi`, `rflags`) are available for use in the test cases and can be modified freely.
A special case are `rsp` and `rbp`, which can be used in the test cases, but their values must always remain within the sandbox (see [Sandbox](./docs/sandbox.md)).

## Vector Registers

Vector registers (`xmm0`-`xmm15`) are also available for use in the test cases.
However, only `xmm0-xmm7` are initialized with input-based values, and the remaining registers are always zero-initialized.

Large-size vector registers (`ymm` and `zmm`) are not supported.
1 change: 1 addition & 0 deletions docs/sandbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UNDER CONSTRUCTION
31 changes: 13 additions & 18 deletions src/x86/executor/code_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,7 @@
// -----------------------------------------------------------------------------------------------
// Note on registers.
// Some of the registers are reserved for a specific purpose and should never be overwritten.
// These include:
// R8 - performance counter #3
// R9 - performance counter #2
// R10 - performance counter #1
// R11 - temporary data for macros
// R12 - SMI counter
// R13 - hardware trace
// R14 - base address of the current actor's main data area
// R15 - base address of the utility area
// See ./docs/registers.md for more information.

#include "code_loader.h"
#include "asm_snippets.h"
Expand Down Expand Up @@ -298,30 +290,33 @@ static inline void prologue(void)
"mov r12, 0\n"
"mov r13, 0\n"

// set HTRACE_REGISTER to -1 to be able to detect missing measurement_start
"mov "HTRACE_REGISTER", -1\n"
// initialize special registers
"mov "HTRACE_REGISTER", 0\n"
"mov "STATUS_REGISTER", 0\n"

"mov rbp, rsp\n"
"sub rsp, 0x1000\n"

// start monitoring interrupts
READ_SMI_START("r12")
READ_SMI_START()
);
}

static inline void epilogue(void)
{
asm_volatile_intel(
READ_SMI_END("r12")
// rbx <- SMI counter
READ_SMI_END()
"mov rbx, "STATUS_REGISTER"\n"
"shr rbx, 32\n"

// rax <- &latest_measurement
"lea rax, [r15 + "xstr(MEASUREMENT_OFFSET)"]\n"

// if we see no interrupts, store the hardware trace (r13)
// otherwise, store zero
"cmp r12, 0; jne 1f \n"
// same goes for uninitialized hardware trace
"cmp "HTRACE_REGISTER", -1; je 1f \n"
// check for errors (detected SMI or incomplete measurement);
// if there are any, we clear the measurement; otherwise, we store the measurements
"cmp rbx, 0; jne 1f \n"
"cmp "STATUS_REGISTER_8", "xstr(SR_ENDED)"; jne 1f \n"
" mov qword ptr [rax + 0x00], r13 \n"
" mov qword ptr [rax + 0x08], r10 \n"
" mov qword ptr [rax + 0x10], r9 \n"
Expand Down
84 changes: 73 additions & 11 deletions src/x86/executor/include/asm_snippets.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,26 @@
// clang-format off

#include "hardware_desc.h"
#include <asm/msr-index.h>

#ifndef VENDOR_ID
#error "VENDOR_ID is not defined! Make sure to include this header late enough."
#endif

/// Reserved registers
#define STATUS_REGISTER "r12"
#define STATUS_REGISTER_32 "r12d"
#define STATUS_REGISTER_8 "r12b"
#define HTRACE_REGISTER "r13"

/// State machine of the tracing process
#define SR_UNINITIALIZED 0
#define SR_STARTED 1
#define SR_ENDED 2
#define SET_SR_STARTED() "mov "STATUS_REGISTER_8", "xstr(SR_STARTED)" \n"
#define SET_SR_ENDED() "mov "STATUS_REGISTER_8", "xstr(SR_ENDED)" \n"


/// Accessors to MSRs
///
// clobber: rax, rcx, rdx
Expand Down Expand Up @@ -59,17 +74,64 @@

/// Detection of System Management Interrupts (SMIs)
///
#if VENDOR_ID == 1 // Intel
#define READ_SMI_START(DEST) READ_MSR_START("0x00000034", DEST)
#define READ_SMI_END(DEST) READ_MSR_END("0x00000034", DEST)

#elif VENDOR_ID == 2 // AMD
#define READ_SMI_START(DEST) \
READ_PFC_ONE("5") \
"sub "DEST", rdx \n"
#define READ_SMI_END(DEST) \
READ_PFC_ONE("5") \
"add "DEST", rdx \n"

/// @brief Clear the upper 32 bits of the STATUS_REGISTER
#define CLEAR_SMI_STATUS() \
"mov "STATUS_REGISTER_32", "STATUS_REGISTER_32" \n"

#if VENDOR_ID == VENDOR_INTEL_
/// @brief Start monitoring SMIs by reading the current value of the SMI counter (MSR 0x34)
/// and storing it in the STATUS_REGISTER[63:32]
/// clobber: rax, rcx, rdx
#define READ_SMI_START() \
"mov rcx, "xstr(MSR_SMI_COUNT)"\n" \
"lfence; rdmsr; lfence \n" \
"mov rcx, 0 \n" \
"sub ecx, eax \n" \
"shl rcx, 32 \n" \
CLEAR_SMI_STATUS() \
"or "STATUS_REGISTER", rcx \n"

/// @brief End monitoring SMIs by reading the current value of the SMI counter (MSR 0x34)
/// and storing the difference between the current and the previous value
/// in the STATUS_REGISTER[31:0]
/// clobber: rax, rcx, rdx
#define READ_SMI_END() \
"mov rcx, "xstr(MSR_SMI_COUNT)"\n" \
"lfence; rdmsr; lfence \n" \
"mov rcx, "STATUS_REGISTER" \n" \
"shr rcx, 32 \n" \
"add ecx, eax \n" \
"shl rcx, 32 \n" \
CLEAR_SMI_STATUS() \
"or "STATUS_REGISTER", rcx \n"
#elif VENDOR_ID == VENDOR_AMD_
/// @brief Start monitoring SMIs by reading the current value of the SMI counter (PMU ID 5)
/// and storing it in the STATUS_REGISTER[63:32]
/// clobber: rax, rcx, rdx
#define READ_SMI_START() \
"mov rcx, 5 \n" \
"lfence; rdpmc; lfence \n" \
"mov rcx, 0 \n" \
"sub ecx, eax \n" \
"shl rcx, 32 \n" \
CLEAR_SMI_STATUS() \
"or "STATUS_REGISTER", rcx \n"

/// @brief End monitoring SMIs by reading the current value of the SMI counter (PMU ID 5)
/// and storing the difference between the current and the previous value
/// in the STATUS_REGISTER[31:0]
/// clobber: rax, rcx, rdx
#define READ_SMI_END() \
"mov rcx, 5 \n" \
"lfence; rdpmc; lfence \n" \
"mov rcx, "STATUS_REGISTER" \n" \
"shr rcx, 32 \n" \
"add ecx, eax \n" \
"shl rcx, 32 \n" \
CLEAR_SMI_STATUS() \
"or "STATUS_REGISTER", rcx \n"

#endif


Expand Down
10 changes: 8 additions & 2 deletions src/x86/executor/include/hardware_desc.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@
#endif

#define VENDOR_INTEL_ 1
#define VENDOR_AMD_ 2
#define VENDOR_AMD_ 2
#undef VENDOR_INTEL
#undef VENDOR_AMD

// Cache configuration
#ifndef L1D_ASSOCIATIVITY
#error "Undefined L1D_ASSOCIATIVITY"
#define L1D_ASSOCIATIVITY 0
#elif L1D_ASSOCIATIVITY != 12 && L1D_ASSOCIATIVITY != 8 && L1D_ASSOCIATIVITY != 4 && L1D_ASSOCIATIVITY != 2
#elif L1D_ASSOCIATIVITY != 12 && L1D_ASSOCIATIVITY != 8 && L1D_ASSOCIATIVITY != 4 && \
L1D_ASSOCIATIVITY != 2
#warning "Unsupported/corrupted L1D associativity. Falling back to 8-way"
#define L1D_ASSOCIATIVITY 8
#endif

// Definitions of MSRs missing in the kernel
#define MSR_SYSCFG 0xc0010010

#endif // _HARDWARE_DESC_H_
2 changes: 1 addition & 1 deletion src/x86/executor/include/macro_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define _MACRO_H_

#include "test_case_parser.h"
#include "asm_snippets.h"
#include <linux/types.h>

typedef enum {
Expand All @@ -31,7 +32,6 @@ typedef enum {
MACRO_SET_DATA_PERMISSIONS = 18,
} macro_name_e;

#define HTRACE_REGISTER "r13"
#define MACRO_PLACEHOLDER_SIZE 8

int expand_macro(tc_symbol_entry_t *macro, uint8_t *dest, uint8_t *macro_dest, size_t *macro_size);
Expand Down
3 changes: 0 additions & 3 deletions src/x86/executor/include/special_registers.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
#include <linux/types.h>
#include "hardware_desc.h"

// A few MSR definitions missing in the kernel
#define MSR_SYSCFG 0xc0010010

typedef struct {
uint64_t cr0;
uint64_t cr4;
Expand Down
Loading

0 comments on commit 126cdeb

Please sign in to comment.