From 914d02825ffd5c0aa39fffec14adbcb73c878536 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Wed, 22 Jun 2011 17:22:57 +0200 Subject: [PATCH] libvassert: vmware VAssert support. - BSD-licensed Code gratefully taken from the project at http://en.sourceforge.jp/projects/sfnet_vassertlinuxsdk/ - For more information on vmware VAssert, a powerful debugging facility usable under vmware, see: www.vmware.com/pdf/ws65_vassert_programming.pdf --- lib/Makefile | 2 +- lib/libvassert/Makefile | 8 + lib/libvassert/backdoor.S | 22 +++ lib/libvassert/vassert.c | 298 ++++++++++++++++++++++++++++++++++++++ lib/libvassert/vassert.h | 138 ++++++++++++++++++ 5 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 lib/libvassert/Makefile create mode 100644 lib/libvassert/backdoor.S create mode 100644 lib/libvassert/vassert.c create mode 100644 lib/libvassert/vassert.h diff --git a/lib/Makefile b/lib/Makefile index afa06c13b..7b884d597 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -21,7 +21,7 @@ SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libdriver libnetdriver \ libddekit .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") -SUBDIR+= libelf libminc libcrypt libterminfo libcurses +SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert .endif .if ${COMPILER_TYPE} == "ack" diff --git a/lib/libvassert/Makefile b/lib/libvassert/Makefile new file mode 100644 index 000000000..6a022c91f --- /dev/null +++ b/lib/libvassert/Makefile @@ -0,0 +1,8 @@ +# Makefile for libvassert library + +LIB= vassert +SRCS= backdoor.S vassert.c +INCS+= vassert.h +INCSDIR= /usr/include/minix + +.include diff --git a/lib/libvassert/backdoor.S b/lib/libvassert/backdoor.S new file mode 100644 index 000000000..2973e265b --- /dev/null +++ b/lib/libvassert/backdoor.S @@ -0,0 +1,22 @@ + +.global libvassert_process_backdoor + +libvassert_process_backdoor: + push %ebx + push %esi + mov 0xc(%esp),%ecx + mov 0x14(%esp),%edx + mov 0x10(%esp),%ebx + mov $0x564d5868,%eax + out %eax,(%dx) + mov 0x18(%esp),%esi + mov %eax,(%esi) + mov 0x20(%esp),%eax + mov %ecx,(%eax) + mov 0x24(%esp),%eax + mov %edx,(%eax) + mov 0x1c(%esp),%eax + mov %ebx,(%eax) + pop %esi + pop %ebx + ret diff --git a/lib/libvassert/vassert.c b/lib/libvassert/vassert.c new file mode 100644 index 000000000..3720551bd --- /dev/null +++ b/lib/libvassert/vassert.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "vassert.h" + +VAssert_StateWrapper vassert_state ALIGNED(VASSERT_PAGE_SIZE); + +#define TRUE 1 +#define FALSE 0 + +#define MAGIC_CMD 0x564d5868 +#define MAGIC_PORT 0x5658 +#define HIGH_BAND_PORT 0x5659 + +#define BACKDOOR_PORT 51 +#define BACKDOOR_HB_PORT 1 +#define CMD_SET_ADDRESS BACKDOOR_PORT|(1<<16) +#define CMD_RETURN_REPLAY BACKDOOR_PORT|(2<<16) +#define CMD_GO_LIVE BACKDOOR_PORT|(3<<16) +#define CMD_LOG BACKDOOR_HB_PORT|(4<<16) +#define CMD_SET_RECORD 47 + +#define LOG_MAX 512 + +typedef char Bool; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +#ifdef VM_X86_64 +typedef uint64 VA; +#else +typedef uint32 VA; +#endif + +static sigjmp_buf segv_jmp; + +/* + *--------------------------------------------------------------------- + * + * sig_segv -- + * + * Customed SEGV signal handler for VAssert_IsInVM. + * + * Results: + * + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------- + */ + +static void +sig_segv(int sig_no) +{ + /* jumping to error handling in VAssert_IsInVM. */ + siglongjmp(segv_jmp, 1); +} + + +/* + *--------------------------------------------------------------------- + * + * VAssert_IsInVM -- + * + * Check if we are in virtual world. + * + * Results: + * + * Return TRUE on success, or FALSE on failure. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------- + */ + +static Bool +VAssert_IsInVM(void) +{ + uint32 eax, ebx, ecx, edx; + static Bool inVM = FALSE; + static Bool tested = FALSE; + if (!tested) { + /* Error handling. */ + if (sigsetjmp(segv_jmp, 0) != 0) { + signal(SIGSEGV, SIG_DFL); + inVM = FALSE; + return inVM; + } + tested = TRUE; + /* Install custom handler. */ + signal(SIGSEGV, sig_segv); + /* Test if we are in a VM. */ + libvassert_process_backdoor(0x0a, 0, MAGIC_PORT, &eax, &ebx, &ecx, &edx); + signal(SIGSEGV, SIG_DFL); + inVM = TRUE; + } + return inVM; +} + + +/* + *--------------------------------------------------------------------- + * + * VAssert_Init -- + * + * Tell vmx that vassert is inited. + * + * Results: + * + * Return 0 on success, or -1 on failure. + * + * Side effects: + * None + * + *--------------------------------------------------------------------- + */ + +char +VAssert_Init(void) +{ + uint32 eax, ebx, ecx, edx; + VA page_address = (VA) &vassert_state.inReplay, ph; + if (!VAssert_IsInVM()) { + return -1; + } + bzero((char*) &vassert_state, sizeof vassert_state); +#ifndef __minix + /* Lock the page. */ + if (mlock(&vassert_state, sizeof vassert_state)) { + return -1; + } +#endif + + /* vmware expects a linear address (or is simply forgetting + * to adjust the given address for segments) + */ + + if(sys_umap(SELF, D, page_address, 1, &ph)) { + printf("VAssert_Init: sys_umap failed\n"); + return -1; + } + + libvassert_process_backdoor(CMD_SET_ADDRESS, ph, + MAGIC_PORT|(1<<16), &eax, &ebx, &ecx, &edx); + + return (eax != -1) ? 0 : -1; +} + + +/* + *--------------------------------------------------------------------- + * + * VAssert_Uninit -- + * + * Tell vmx that vassert is finalized. + * + * Results: + * + * Return 0 on success, or -1 on failure. + * + * Side effects: + * None + * + *--------------------------------------------------------------------- + */ + +char +VAssert_Uninit(void) +{ + unsigned int eax, ebx, ecx, edx; + if (!VAssert_IsInVM()) { + return -1; + } + libvassert_process_backdoor(CMD_SET_ADDRESS, 0, MAGIC_PORT|(0<<16), &eax, &ebx, &ecx, &edx); + return (eax != -1) ? 0 : 1; +} + + +/* + *--------------------------------------------------------------------- + * + * VAssert_LogMain -- + * + * Print message to a text file on host side. + * + * Results: + * None + * + * Side effects: + * Write to a text file with fixed name. + * If the file exists, host UI will ask for append/replace/ignore + * + *--------------------------------------------------------------------- + */ + +void +VAssert_LogMain(const char *format, ...) +{ + unsigned int eax, ebx, ecx, edx; + char buf[LOG_MAX]; + unsigned int len = 0; + va_list ap; + va_start(ap, format); + len = vsnprintf(buf, LOG_MAX, format, ap); + va_end(ap); + __asm__ __volatile__("cld; rep outsb;" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "0"(MAGIC_CMD), "1"(CMD_LOG), "2"(len), "d"(HIGH_BAND_PORT), "S"(buf) + : "memory" + ); +} + + +/* + *--------------------------------------------------------------------- + * + * VAssert_GoLiveMain -- + * + * Make the vm which is in replay exit replay. + * + * Results: + * None + * + * Side effects: + * Replay is stopped. + * + *--------------------------------------------------------------------- + */ + +void +VAssert_GoLiveMain() +{ + unsigned int eax, ebx, ecx, edx; + vassert_state.inReplay = 0; + libvassert_process_backdoor(CMD_GO_LIVE, 0, MAGIC_PORT, &eax, &ebx, &ecx, &edx); +} + + +/* + *--------------------------------------------------------------------- + * + * VAssert_ReturnToReplayMain -- + * + * Called after the custom work is done, and replay is to continue. + * + * Results: + * None + * + * Side effects: + * Replay is continued from pause. + * + *--------------------------------------------------------------------- + */ + +void +VAssert_ReturnToReplayMain() +{ + unsigned int eax, ebx, ecx, edx; + libvassert_process_backdoor(CMD_RETURN_REPLAY, 0, MAGIC_PORT, &eax, &ebx, &ecx, &edx); +} + + +/* + *--------------------------------------------------------------------- + * + * VAssert_SetRecordingMain -- + * + * Ask vmx for starting or stopping recording. + * + * Results: + * + * Return TRUE on success, or FALSE on failure. + * + * Side effects: + * Recording is started or stopped. + * + *--------------------------------------------------------------------- + */ + +char +VAssert_SetRecordingMain(char start) +{ + uint32 eax, ebx, ecx, edx; + if (!VAssert_IsInVM()) { + return FALSE; + } + libvassert_process_backdoor(CMD_SET_RECORD, start ? 1 : 2, MAGIC_PORT, &eax, &ebx, &ecx, &edx); + return (eax == 1) ? TRUE : FALSE; +} + diff --git a/lib/libvassert/vassert.h b/lib/libvassert/vassert.h new file mode 100644 index 000000000..a18c56443 --- /dev/null +++ b/lib/libvassert/vassert.h @@ -0,0 +1,138 @@ +#ifndef _VASSERT_H_ +#define _VASSERT_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /*__cplusplus*/ + +#define ALIGNED(n) __attribute__((__aligned__(n))) +#define VASSERT_TRIGGER_OFFSET 1221 +#define VASSERT_PAGE_SIZE 4096 + +/* Need to align at 4K. */ +/* Ensure the inReplay flag is on its own page. */ +#pragma pack(1) +typedef struct VAssert_StateWrapper { + char space1[VASSERT_TRIGGER_OFFSET]; + volatile char inReplay; + char space[VASSERT_PAGE_SIZE - VASSERT_TRIGGER_OFFSET - sizeof(char)]; +} VAssert_StateWrapper; +#pragma pack() + +extern VAssert_StateWrapper vassert_state; + +/* + * User-selectable standard functions. + * XXX: Document these, in coordination with the SDK docs. + */ + +#if defined(__KERNEL__) +# define KERNEL_VASSERT +#endif + +#ifdef KERNEL_VASSERT + +# ifndef VASSERT_CUSTOM_ASSERT +# define VASSERT_CUSTOM_ASSERT(expr) +# endif + +# ifndef VASSERT_CUSTOM_ABORT +# include +# define VASSERT_CUSTOM_ABORT() ((void)0) // printk(KERN_ALERT"VAssert abort at %s: %d", __FILE__, __LINE__) +# endif + +# ifndef VASSERT_CUSTOM_LOG +# include +# define VASSERT_CUSTOM_LOG printk +# endif + +#else +# ifndef VASSERT_CUSTOM_ASSERT +# include +# define VASSERT_CUSTOM_ASSERT assert +# endif + +# ifndef VASSERT_CUSTOM_ABORT +# include +# define VASSERT_CUSTOM_ABORT abort +# endif + +# ifndef VASSERT_CUSTOM_LOG +# include +# define VASSERT_CUSTOM_LOG printf +# endif +#endif + +/* Results: 0 if successful, -1 if not. */ +// XXX need to automatic de-register trigger page when the program quits +extern char VAssert_Init(void); +extern char VAssert_Uninit(void); + +/* + * These functions should not be called directly; they need to be wrapped. + */ +extern void VAssert_LogMain(const char *format, ...); +extern void VAssert_GoLiveMain(void); +extern void VAssert_ReturnToReplayMain(void); +extern char VAssert_Trace(size_t max_size); + +#ifdef VASSERT_ALWAYS_EXECUTE + +#define VAssert_Assert(expr) \ +do { \ + VASSERT_CUSTOM_ASSERT(expr); \ +} while (0) + +#define VAssert_Log(args) \ +do { \ + VASSERT_CUSTOM_LOG args; \ +} while (0) + +#define VAssert_BeginBlock +#define VAssert_EndBlock + +#else /* VASSERT_ALWAYS_EXECUTE */ + +#define VAssert_Assert(expr) \ +do { \ + if (vassert_state.inReplay) { \ + if (!(expr)) { \ + VAssert_GoLiveMain(); \ + VASSERT_CUSTOM_ABORT(); \ + } else { \ + VAssert_ReturnToReplayMain(); \ + } \ + } \ +} while (0) + +#define VAssert_Log(args) \ +do { \ + if (vassert_state.inReplay) { \ + VAssert_LogMain args; \ + VAssert_ReturnToReplayMain(); \ + } \ +} while (0) + +#define VAssert_BeginBlock if (vassert_state.inReplay) +#define VAssert_EndBlock VAssert_ReturnToReplayMain() + +#endif /* VASSERT_ALWAYS_EXECUTE */ + +/* + * Record/Replay functionality + */ +/* + * Results: True if successful, false if not. + * Input: True to start recording, false to stop. + */ +extern char VAssert_SetRecordingMain(char start); + +#define VAssert_StartRecording() VAssert_SetRecordingMain(1) +#define VAssert_StopRecording() VAssert_SetRecordingMain(0) + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*_VASSERT_H_*/