diff --git a/simulators/bochs/cpu/cpu.cc b/simulators/bochs/cpu/cpu.cc index 7f259f1f..35db3904 100644 --- a/simulators/bochs/cpu/cpu.cc +++ b/simulators/bochs/cpu/cpu.cc @@ -33,7 +33,7 @@ // Just a dummy function to define a join-point. This function is // *just* called once within bx_cpu_c::cpu_loop(...). -static inline void defineCPULoopJoinPoint(BX_CPU_C* pThis, bxICacheEntry_c *pEntry) +static inline void defineCPULoopJoinPoint(BX_CPU_C* pThis, bxInstruction_c *pInstr) { /* nothing to do here */ } @@ -166,7 +166,7 @@ void BX_CPU_C::cpu_loop(Bit32u max_instr_count) * */ - defineCPULoopJoinPoint(BX_CPU_THIS, entry); + defineCPULoopJoinPoint(BX_CPU_THIS, i); /****************************************************************/ // instruction decoding completed -> continue with execution diff --git a/src/core/sal/bochs/BochsController.cc b/src/core/sal/bochs/BochsController.cc index a796cae2..44c00646 100644 --- a/src/core/sal/bochs/BochsController.cc +++ b/src/core/sal/bochs/BochsController.cc @@ -19,7 +19,7 @@ bx_bool interrupt_injection_request = false; BochsController::BochsController() : SimulatorController(new BochsRegisterManager(), new BochsMemoryManager()), - m_CPUContext(NULL), m_CacheEntry(NULL) + m_CPUContext(NULL), m_CurrentInstruction(NULL) { // ------------------------------------- // Add the general purpose register: @@ -116,12 +116,12 @@ void BochsController::onBreakpoint(address_t instrPtr, address_t address_space) // implementation. } -void BochsController::updateBPEventInfo(BX_CPU_C *context, bxICacheEntry_c *cacheEntry) +void BochsController::updateBPEventInfo(BX_CPU_C *context, bxInstruction_c *instr) { assert(context != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!"); - assert(cacheEntry != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!"); + assert(instr != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!"); m_CPUContext = context; - m_CacheEntry = cacheEntry; + m_CurrentInstruction = instr; } void BochsController::onIOPort(unsigned char data, unsigned port, bool out) { @@ -229,7 +229,7 @@ void BochsController::onTimerTrigger(void* thisPtr) const std::string& BochsController::getMnemonic() const { static std::string str; - bxInstruction_c* pInstr = getICacheEntry()->i; + bxInstruction_c* pInstr = getCurrentInstruction(); assert(pInstr != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!"); const char* pszName = get_bx_opcode_name(pInstr->getIaOpcode()); if (pszName != NULL) diff --git a/src/core/sal/bochs/BochsController.hpp b/src/core/sal/bochs/BochsController.hpp index 159fff74..d01cc730 100644 --- a/src/core/sal/bochs/BochsController.hpp +++ b/src/core/sal/bochs/BochsController.hpp @@ -31,7 +31,7 @@ class BochsController : public SimulatorController { private: ExperimentFlow* m_CurrFlow; //!< Stores the current flow for save/restore-operations BX_CPU_C *m_CPUContext; //!< Additional information that is passed on occurence of a BPEvent - bxICacheEntry_c *m_CacheEntry; //!< dito. + bxInstruction_c *m_CurrentInstruction; //!< dito. #ifdef DEBUG unsigned m_Regularity; //! regularity of instruction ptr output unsigned m_Counter; //! current instr-ptr counter @@ -145,7 +145,7 @@ public: * Retrieves the current Bochs instruction cache entry * @returns a pointer to a bxICacheEntry_c object */ - inline bxICacheEntry_c *getICacheEntry() const { return m_CacheEntry; } + inline bxInstruction_c *getCurrentInstruction() const { return m_CurrentInstruction; } /** * Retrieves the current CPU context * @return a pointer to a \c BX_CPU_C object @@ -157,7 +157,7 @@ public: * @param context the CPU context object ptr (Bochs internal= * @param cacheEntry the Bochs internal CPU cache entry ptr */ - void updateBPEventInfo(BX_CPU_C *context, bxICacheEntry_c *cacheEntry); + void updateBPEventInfo(BX_CPU_C *context, bxInstruction_c *instr); }; } // end-of-namespace: fail diff --git a/src/core/sal/bochs/Breakpoints.ah b/src/core/sal/bochs/Breakpoints.ah index 0c7e45d1..768a5705 100644 --- a/src/core/sal/bochs/Breakpoints.ah +++ b/src/core/sal/bochs/Breakpoints.ah @@ -20,10 +20,10 @@ aspect Breakpoints { // BX_CPU(0) otherwise BX_CPU_C* pThis = *(tjp->arg<0>()); // Points to the *current* bxInstruction-object - bxICacheEntry_c* pEntry = *(tjp->arg<1>()); + bxInstruction_c* pInstr = *(tjp->arg<1>()); // Report this event to the Bochs controller: - fail::simulator.updateBPEventInfo(pThis, pEntry); + fail::simulator.updateBPEventInfo(pThis, pInstr); fail::simulator.onBreakpoint(pThis->get_instruction_pointer(), pThis->cr3); // Note: get_bx_opcode_name(pInstr->getIaOpcode()) retrieves the mnemonics. } diff --git a/src/experiments/l4-sys/CMakeLists.txt b/src/experiments/l4-sys/CMakeLists.txt index 96cfda83..61112e19 100644 --- a/src/experiments/l4-sys/CMakeLists.txt +++ b/src/experiments/l4-sys/CMakeLists.txt @@ -11,6 +11,7 @@ set(MY_PROTOS set(MY_CAMPAIGN_SRCS aluinstr.hpp + aluinstr.cc experiment.hpp experiment.cc campaign.hpp diff --git a/src/experiments/l4-sys/UDIS86.cc b/src/experiments/l4-sys/UDIS86.cc index 2dc12c08..368ffafd 100644 --- a/src/experiments/l4-sys/UDIS86.cc +++ b/src/experiments/l4-sys/UDIS86.cc @@ -3,7 +3,7 @@ using namespace fail; -Udis86::Udis86(const unsigned char *instr, size_t size, address_t ip) { +Udis86::Udis86(unsigned char const *instr, size_t size, address_t ip) { // initialise the buffer udis_instr_size = size; udis_instr = static_cast(malloc(udis_instr_size)); diff --git a/src/experiments/l4-sys/UDIS86.hpp b/src/experiments/l4-sys/UDIS86.hpp index e546bfc9..88c9ec1d 100644 --- a/src/experiments/l4-sys/UDIS86.hpp +++ b/src/experiments/l4-sys/UDIS86.hpp @@ -20,13 +20,13 @@ private: unsigned char *udis_instr; // +#include +#include +#include "config.h" +#include "cpu/instr.h" /** * Trying to order X86 ALU instructions. * Each instruction class contains instructions * of roughly equal length and operands, so that * all the bxInstruction_c structures residing within - * one class contain the same member fields. + * one class contain the same member fields (except for + * b1, rm and the execute pointers). */ enum X86AluClass { ALU_UNDEF = 0, + ALU_GENERIC, ALU_RM8, ALU_RM16, ALU_RM32, - ALU_REG, - ALU_IMM8_REG, - ALU_IMM16_REG, - ALU_IMM32_REG, + ALU_IMM8, + ALU_IMM16, + ALU_IMM32, ALU_IMM8_RM8, ALU_IMM8_RM16, ALU_IMM8_RM32, ALU_IMM16_RM16, - ALU_IMM32_RM32, - ALU_REG_RM8, - ALU_REG_RM16, - ALU_REG_RM32 + ALU_IMM32_RM32 }; /** @@ -48,28 +43,32 @@ struct BochsALUInstr { */ Bit16u bochs_operation; /** - * the x86 opcode (known as b1 in bxInstruction_c) + * the x86 opcode, as stored by Bochs (known as b1 in bxInstruction_c) */ Bit8u opcode; /** * the reg part of the modr/m field (known as "nnn" in bxInstruction_c) * it is used to - * a) further subdivide the functionality of a given opcode - * b) specify a register the instruction is supposed to use - * In this class, a value of 8 or higher marks this field unused. + * a) further subdivide the functionality of a given opcode (reg < REG_COUNT) + * b) specify a register the instruction is supposed to use (reg = REG_COUNT) + * In this class, a value greater than REG_COUNT marks this field unused. */ Bit8u reg; /** * the register offset of the instruction byte of this instruction * Some x86 instructions, like INC register, add a register offset * to their opcode. This offset is stored as "rm" in bxInstruction_c. - * In this class, a value of 8 or higher marks this field unused. + * In this class, a value greater than REG_COUNT marks this field unused. */ Bit8u opcodeRegisterOffset; /** * the ALU class this instruction belongs to */ X86AluClass aluClass; + /** + * the count of registers + */ + static const unsigned REG_COUNT = 8; }; @@ -86,43 +85,54 @@ const BochsALUInstr aluInstructions [] = { /* --- /// UNARY OPERATIONS \\\ --- */ // 8-bit - { BX_IA_INC_Eb, 0xFE, 0, 8, ALU_RM8}, - { BX_IA_DEC_Eb, 0xFE, 1, 8, ALU_RM8}, - { BX_IA_NOT_Eb, 0xF6, 2, 8, ALU_RM8}, - { BX_IA_NEG_Eb, 0xF6, 3, 8, ALU_RM8}, + { BX_IA_INC_Eb, 0xFE, 0, BochsALUInstr::REG_COUNT + 1, ALU_RM8}, + { BX_IA_DEC_Eb, 0xFE, 1, BochsALUInstr::REG_COUNT + 1, ALU_RM8}, + { BX_IA_NOT_Eb, 0xF6, 2, BochsALUInstr::REG_COUNT + 1, ALU_RM8}, + { BX_IA_NEG_Eb, 0xF6, 3, BochsALUInstr::REG_COUNT + 1, ALU_RM8}, // 16-bit - { BX_IA_INC_Ew, 0xFF, 0, 8, ALU_RM16}, - { BX_IA_DEC_Ew, 0xFF, 1, 8, ALU_RM16}, - { BX_IA_NOT_Ew, 0xF7, 2, 8, ALU_RM16}, - { BX_IA_NEG_Ew, 0xF7, 3, 8, ALU_RM16}, + { BX_IA_INC_Ew, 0xFF, 0, BochsALUInstr::REG_COUNT + 1, ALU_RM16}, + { BX_IA_DEC_Ew, 0xFF, 1, BochsALUInstr::REG_COUNT + 1, ALU_RM16}, + { BX_IA_NOT_Ew, 0xF7, 2, BochsALUInstr::REG_COUNT + 1, ALU_RM16}, + { BX_IA_NEG_Ew, 0xF7, 3, BochsALUInstr::REG_COUNT + 1, ALU_RM16}, // 32-bit - { BX_IA_INC_Ed, 0xFF, 0, 8, ALU_RM32}, - { BX_IA_DEC_Ed, 0xFF, 1, 8, ALU_RM32}, - { BX_IA_NOT_Ed, 0xF7, 2, 8, ALU_RM32}, - { BX_IA_NEG_Ed, 0xF7, 3, 8, ALU_RM32}, + { BX_IA_INC_Ed, 0xFF, 0, BochsALUInstr::REG_COUNT + 1, ALU_RM32}, + { BX_IA_DEC_Ed, 0xFF, 1, BochsALUInstr::REG_COUNT + 1, ALU_RM32}, + { BX_IA_NOT_Ed, 0xF7, 2, BochsALUInstr::REG_COUNT + 1, ALU_RM32}, + { BX_IA_NEG_Ed, 0xF7, 3, BochsALUInstr::REG_COUNT + 1, ALU_RM32}, // register - { BX_IA_INC_RX, 0x40, 8, 7, ALU_REG}, - { BX_IA_DEC_RX, 0x48, 8, 7, ALU_REG}, - { BX_IA_INC_ERX, 0x40, 8, 7, ALU_REG}, - { BX_IA_DEC_ERX, 0x48, 8, 7, ALU_REG}, + { BX_IA_INC_RX, 0x40, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT, ALU_GENERIC}, + { BX_IA_DEC_RX, 0x48, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT, ALU_GENERIC}, + { BX_IA_INC_ERX, 0x40, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT, ALU_GENERIC}, + { BX_IA_DEC_ERX, 0x48, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT, ALU_GENERIC}, /* --- \\\ UNARY OPERATIONS /// --- */ + /* --- /// BCD ADJUSTMENT \\\ --- */ + + { BX_IA_DAA, 0x27, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_GENERIC}, + { BX_IA_DAS, 0x2F, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_GENERIC}, + { BX_IA_AAA, 0x37, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_GENERIC}, + { BX_IA_AAS, 0x3F, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_GENERIC}, + { BX_IA_AAM, 0xD4, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM8 }, + { BX_IA_AAD, 0xD5, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM8 }, + + /* --- \\\ BCD ADJUSTMENT /// --- */ + /* --- /// SHIFT OPERATIONS \\\ --- */ // a macro to reduce copy-paste overhead #define SHIFTOPS(OPCODE, WD, CLASS) \ - { BX_IA_RCL_E##WD, OPCODE, 2, 8, CLASS }, \ - { BX_IA_RCR_E##WD, OPCODE, 3, 8, CLASS }, \ - { BX_IA_ROL_E##WD, OPCODE, 0, 8, CLASS }, \ - { BX_IA_ROR_E##WD, OPCODE, 1, 8, CLASS }, \ - { BX_IA_SHL_E##WD, OPCODE, 4, 8, CLASS }, \ - { BX_IA_SAR_E##WD, OPCODE, 7, 8, CLASS }, \ - { BX_IA_SHL_E##WD, OPCODE, 6, 8, CLASS }, \ - { BX_IA_SHR_E##WD, OPCODE, 5, 8, CLASS } + { BX_IA_RCL_E##WD, OPCODE, 2, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_RCR_E##WD, OPCODE, 3, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_ROL_E##WD, OPCODE, 0, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_ROR_E##WD, OPCODE, 1, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_SHL_E##WD, OPCODE, 4, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_SAR_E##WD, OPCODE, 7, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_SHL_E##WD, OPCODE, 6, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_SHR_E##WD, OPCODE, 5, BochsALUInstr::REG_COUNT + 1, CLASS } // first shifting by one bit SHIFTOPS(0xD0, b, ALU_RM8), @@ -130,24 +140,35 @@ const BochsALUInstr aluInstructions [] = { SHIFTOPS(0xD1, d, ALU_RM32), // then shifting by CL bits - SHIFTOPS(0xD2, b, ALU_REG_RM8), - SHIFTOPS(0xD3, w, ALU_REG_RM16), - SHIFTOPS(0xD3, d, ALU_REG_RM32), + SHIFTOPS(0xD2, b, ALU_RM8), + SHIFTOPS(0xD3, w, ALU_RM16), + SHIFTOPS(0xD3, d, ALU_RM32), // then shifting by a number of bits given via an immediate value SHIFTOPS(0xC0, b, ALU_IMM8_RM8), SHIFTOPS(0xC1, w, ALU_IMM8_RM16), SHIFTOPS(0xC1, d, ALU_IMM8_RM32), + #undef SHIFTOPS + // SHLD / SHRD (Note: Bochs Opcode; normally 0x0F...) + { BX_IA_SHLD_EwGw, 0xA4, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM16 }, + { BX_IA_SHLD_EdGd, 0xA4, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM32 }, + { BX_IA_SHLD_EwGw, 0xA5, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM16 }, + { BX_IA_SHLD_EdGd, 0xA5, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM32 }, + { BX_IA_SHRD_EwGw, 0xAC, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM16 }, + { BX_IA_SHRD_EdGd, 0xAC, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM32 }, + { BX_IA_SHRD_EwGw, 0xAD, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM16 }, + { BX_IA_SHRD_EdGd, 0xAD, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM32 }, + /* --- \\\ SHIFT OPERATIONS /// --- */ /* --- /// BINARY OPERATIONS \\\ --- */ // register ax, immediate #define BINOPS(IACODE, OPCODE8, OPCODE16) \ - { BX_IA_##IACODE##_ALIb, OPCODE8, 8, 8, ALU_IMM8_REG }, \ - { BX_IA_##IACODE##_AXIw, OPCODE16, 8, 8, ALU_IMM16_REG }, \ - { BX_IA_##IACODE##_EAXId, OPCODE16, 8, 8, ALU_IMM32_REG } + { BX_IA_##IACODE##_ALIb, OPCODE8, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM8 }, \ + { BX_IA_##IACODE##_AXIw, OPCODE16, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM16 }, \ + { BX_IA_##IACODE##_EAXId, OPCODE16, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM32 } BINOPS(ADC, 0x14, 0x15), BINOPS(ADD, 0x04, 0x05), @@ -161,27 +182,28 @@ const BochsALUInstr aluInstructions [] = { // r/m, immediate #define BINOPS(OPCODE, WDE, WDI, CLASS) \ - { BX_IA_ADC_E##WDE##I##WDI, OPCODE, 2, 8, CLASS }, \ - { BX_IA_ADD_E##WDE##I##WDI, OPCODE, 0, 8, CLASS }, \ - { BX_IA_AND_E##WDE##I##WDI, OPCODE, 4, 8, CLASS }, \ - { BX_IA_CMP_E##WDE##I##WDI, OPCODE, 7, 8, CLASS }, \ - { BX_IA_OR_E##WDE##I##WDI, OPCODE, 1, 8, CLASS }, \ - { BX_IA_SBB_E##WDE##I##WDI, OPCODE, 3, 8, CLASS }, \ - { BX_IA_SUB_E##WDE##I##WDI, OPCODE, 5, 8, CLASS }, \ - { BX_IA_XOR_E##WDE##I##WDI, OPCODE, 6, 8, CLASS } + { BX_IA_ADC_E##WDE##I##WDI, OPCODE, 2, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_ADD_E##WDE##I##WDI, OPCODE, 0, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_AND_E##WDE##I##WDI, OPCODE, 4, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_CMP_E##WDE##I##WDI, OPCODE, 7, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_OR_E##WDE##I##WDI, OPCODE, 1, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_SBB_E##WDE##I##WDI, OPCODE, 3, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_SUB_E##WDE##I##WDI, OPCODE, 5, BochsALUInstr::REG_COUNT + 1, CLASS }, \ + { BX_IA_XOR_E##WDE##I##WDI, OPCODE, 6, BochsALUInstr::REG_COUNT + 1, CLASS } BINOPS(0x80, b, b, ALU_IMM8_RM8), BINOPS(0x81, w, w, ALU_IMM16_RM16), BINOPS(0x81, d, d, ALU_IMM32_RM32), + BINOPS(0x82, b, b, ALU_IMM8_RM8), BINOPS(0x83, w, w, ALU_IMM8_RM16), BINOPS(0x83, d, d, ALU_IMM8_RM32), #undef BINOPS // r/m, arbitrary register #define BINOPS(IACODE, OPCODE8, OPCODE16) \ - { BX_IA_##IACODE##_EbGb, OPCODE8, 7, 8, ALU_REG_RM8 }, \ - { BX_IA_##IACODE##_EwGw, OPCODE16, 7, 8, ALU_REG_RM16 }, \ - { BX_IA_##IACODE##_EdGd, OPCODE16, 7, 8, ALU_REG_RM32 } + { BX_IA_##IACODE##_EbGb, OPCODE8, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM8 }, \ + { BX_IA_##IACODE##_EwGw, OPCODE16, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM16 }, \ + { BX_IA_##IACODE##_EdGd, OPCODE16, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM32 } BINOPS(ADC, 0x10, 0x11), BINOPS(ADD, 0x00, 0x01), @@ -195,21 +217,83 @@ const BochsALUInstr aluInstructions [] = { // arbitrary register, r/m #define BINOPS(IACODE, OPCODE8, OPCODE16) \ - { BX_IA_##IACODE##_GbEb, OPCODE8, 7, 8, ALU_REG_RM8 }, \ - { BX_IA_##IACODE##_GwEw, OPCODE16, 7, 8, ALU_REG_RM16 }, \ - { BX_IA_##IACODE##_GdEd, OPCODE16, 7, 8, ALU_REG_RM32 } + { BX_IA_##IACODE##_GbEb, OPCODE8, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM8 }, \ + { BX_IA_##IACODE##_GwEw, OPCODE16, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM16 }, \ + { BX_IA_##IACODE##_GdEd, OPCODE16, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM32 } BINOPS(ADC, 0x12, 0x13), BINOPS(ADD, 0x02, 0x03), BINOPS(AND, 0x22, 0x23), - BINOPS(CMP, 0x3a, 0x3b), - BINOPS(OR, 0x0a, 0x0b), - BINOPS(SBB, 0x1a, 0x1b), - BINOPS(SUB, 0x2a, 0x2b), + BINOPS(CMP, 0x3A, 0x3B), + BINOPS(OR, 0x0A, 0x0B), + BINOPS(SBB, 0x1A, 0x1B), + BINOPS(SUB, 0x2A, 0x2B), BINOPS(XOR, 0x32, 0x33), #undef BINOPS + // TEST instruction + { BX_IA_TEST_ALIb, 0xA8, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM8}, + { BX_IA_TEST_AXIw, 0xA9, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM16}, + { BX_IA_TEST_EAXId, 0xA9, BochsALUInstr::REG_COUNT + 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM32}, + { BX_IA_TEST_EbIb, 0xF6, 0, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM8 }, + { BX_IA_TEST_EbIb, 0xF6, 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM8 }, + { BX_IA_TEST_EwIw, 0xF7, 0, BochsALUInstr::REG_COUNT + 1, ALU_IMM16_RM16 }, + { BX_IA_TEST_EwIw, 0xF7, 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM16_RM16 }, + { BX_IA_TEST_EdId, 0xF7, 0, BochsALUInstr::REG_COUNT + 1, ALU_IMM32_RM32 }, + { BX_IA_TEST_EdId, 0xF7, 1, BochsALUInstr::REG_COUNT + 1, ALU_IMM32_RM32 }, + { BX_IA_TEST_EbGb, 0x84, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM8}, + { BX_IA_TEST_EwGw, 0x85, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM16}, + { BX_IA_TEST_EdGd, 0x85, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM32}, + + // MUL and DIV +#define BINOPS(IACODE, OPCODE8, OPCODE16, REG) \ + { BX_IA_##IACODE##_ALEb, OPCODE8, REG, BochsALUInstr::REG_COUNT + 1, ALU_IMM8 }, \ + { BX_IA_##IACODE##_AXEw, OPCODE16, REG, BochsALUInstr::REG_COUNT + 1, ALU_IMM16 }, \ + { BX_IA_##IACODE##_EAXEd, OPCODE16, REG, BochsALUInstr::REG_COUNT + 1, ALU_IMM32 } + BINOPS(MUL, 0xF6, 0xF7, 4), + BINOPS(IMUL, 0xF6, 0xF7, 5), + BINOPS(DIV, 0xF6, 0xF7, 6), + BINOPS(IDIV, 0xF6, 0xF7, 7), +#undef BINOPS + { BX_IA_IMUL_GwEwIw, 0x69, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM16_RM16 }, + { BX_IA_IMUL_GwEwIw, 0x69, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM32_RM32 }, + { BX_IA_IMUL_GwEwIw, 0x6B, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM16 }, + { BX_IA_IMUL_GwEwIw, 0x6B, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_IMM8_RM32 }, + // two-byte opcode, see above + { BX_IA_IMUL_GwEw, 0xAF, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM16}, + { BX_IA_IMUL_GdEd, 0xAF, BochsALUInstr::REG_COUNT, BochsALUInstr::REG_COUNT + 1, ALU_RM32}, + /* --- \\\ BINARY OPERATIONS /// --- */ }; +const size_t aluInstructionsSize = sizeof(aluInstructions); + +class BochsALUInstructions { +public: + /** + * + */ + typedef std::vector InstrList; + typedef std::map EquivClassMap; + /** + * + */ + BochsALUInstructions(const BochsALUInstr *initial_array, size_t array_size); + ~BochsALUInstructions() { free(allInstr); } + bool isALUInstruction(const bxInstruction_c *src); + bxInstruction_c randomEquivalent() const; +protected: + void bochsInstrToInstrStruct(const bxInstruction_c *src, BochsALUInstr *dest) const; +private: + BochsALUInstr *allInstr; + BochsALUInstr lastInstr; + const bxInstruction_c *lastOrigInstr; + size_t allInstrSize; + EquivClassMap equivalenceClasses; + void buildEquivalenceClasses(); +#ifdef DEBUG + void printNestedMap(); +#endif +}; + #endif // __L4SYS_ALUINSTR_HPP__ diff --git a/src/experiments/l4-sys/campaign.cc b/src/experiments/l4-sys/campaign.cc index 3bf46b99..eaabcbc7 100644 --- a/src/experiments/l4-sys/campaign.cc +++ b/src/experiments/l4-sys/campaign.cc @@ -11,16 +11,16 @@ using namespace std; using namespace fail; char const * const results_csv = "l4sys.csv"; -const char *l4sys_output_result_strings[] = { "Unknown", "Done", "Timeout", "Trap", "Interrupt", "Wrong output", "Error" }; -const char *l4sys_output_experiment_strings[] = { "Unknown", "GPR Flip", "RAT Flip", "Instr Flip", "ALU Instr Flip" }; +char const *l4sys_output_result_strings[] = { "Unknown", "Done", "Incomplete", "Timeout", "Wrong output", "Error" }; +char const *l4sys_output_experiment_strings[] = { "Unknown", "GPR Flip", "RAT Flip", "Instr Flip", "ALU Instr Flip" }; +char const *l4sys_output_register_strings[] = { "Unknown", "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI" }; #define OUTPUT_CASE(OUTPUT) case L4SysProtoMsg::OUTPUT: return l4sys_output_result_strings[L4SysProtoMsg::OUTPUT]; std::string L4SysCampaign::output_result(L4SysProtoMsg_ResultType res) { switch (res) { OUTPUT_CASE(DONE); + OUTPUT_CASE(INCOMPLETE); OUTPUT_CASE(TIMEOUT); - OUTPUT_CASE(TRAP); - OUTPUT_CASE(INTR); OUTPUT_CASE(WRONG); OUTPUT_CASE(UNKNOWN); default: @@ -61,9 +61,23 @@ bool L4SysCampaign::run() { int count = 0; srand(time(NULL)); + for (int i = 0; i < 1000; ++i) { + L4SysExperimentData *d = new L4SysExperimentData; + d->msg.set_exp_type(d->msg.ALUINSTR); + // modify for a random instruction + int instr_offset = rand() % L4SYS_NUMINSTR; + d->msg.set_instr_offset(instr_offset); + // this value is not required for this experiment, so set it to an arbitrary value + d->msg.set_bit_offset(0); + + campaignmanager.addParam(d); + ++count; + } +#if 0 for (int i = 0; i < 25; ++i) { L4SysExperimentData *d = new L4SysExperimentData; - d->msg.set_exp_type(d->msg.IDCFLIP); + d->msg.set_exp_type(d->msg.GPRFLIP); + d->msg.set_register_offset(d->msg.EBX); // modify for a random instruction int instr_offset = rand() % L4SYS_NUMINSTR; d->msg.set_instr_offset(instr_offset); @@ -74,7 +88,6 @@ bool L4SysCampaign::run() { campaignmanager.addParam(d); ++count; } -#if 0 for (int i = 0; i < 1000; ++i) { L4SysExperimentData *d = new L4SysExperimentData; d->msg.set_exp_type(d->msg.RATFLIP); @@ -108,13 +121,17 @@ bool L4SysCampaign::run() { L4SysExperimentData *res; int rescount = 0; results - << "exp_type,injection_ip,instr_offset,injection_bit,resulttype,resultdata,output,details" + << "exp_type,injection_ip,register,instr_offset,injection_bit,resulttype,resultdata,output,details" << endl; while ((res = static_cast(campaignmanager.getDone()))) { rescount++; - results << output_experiment(res->msg.exp_type()) << "," << hex << res->msg.injection_ip() << "," << dec - << res->msg.instr_offset() << "," << res->msg.bit_offset() + results << output_experiment(res->msg.exp_type()) << "," << hex << res->msg.injection_ip() << dec << ","; + if (res->msg.has_register_offset()) + results << res->msg.register_offset(); + else + results << "None"; + results << "," << res->msg.instr_offset() << "," << res->msg.bit_offset() << "," << output_result(res->msg.resulttype()) << "," << res->msg.resultdata(); if (res->msg.has_output()) diff --git a/src/experiments/l4-sys/experiment.cc b/src/experiments/l4-sys/experiment.cc index c5043516..1f303d5b 100644 --- a/src/experiments/l4-sys/experiment.cc +++ b/src/experiments/l4-sys/experiment.cc @@ -9,6 +9,7 @@ #include "experiment.hpp" #include "experimentInfo.hpp" #include "UDIS86.hpp" +#include "aluinstr.hpp" #include "campaign.hpp" #include "sal/SALConfig.hpp" @@ -26,9 +27,8 @@ using namespace fail; // Check if configuration dependencies are satisfied: #if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \ !defined(CONFIG_SR_SAVE) || \ - !defined(CONFIG_EVENT_TRAP) || !defined(CONFIG_EVENT_IOPORT) || \ - !defined(CONFIG_EVENT_INTERRUPT) -#error This experiment needs: breakpoints, suppressed-interrupts, traps, I/O port and interrupt events, \ + !defined(CONFIG_EVENT_IOPORT) +#error This experiment needs: breakpoints and I/O port events, \ save, and restore. Enable these in the configuration. #endif @@ -149,6 +149,26 @@ void L4SysExperiment::singleStep() { simulator.removeListener(&singlestepping_event); } +void L4SysExperiment::injectInstruction(bxInstruction_c *oldInstr, bxInstruction_c *newInstr) { + // backup the current and insert the faulty instruction + bxInstruction_c backupInstr; + memcpy(&backupInstr, oldInstr, sizeof(bxInstruction_c)); + memcpy(oldInstr, newInstr, sizeof(bxInstruction_c)); + + // execute the faulty instruction, then return + singleStep(); + + //restore the old instruction + memcpy(oldInstr, &backupInstr, sizeof(bxInstruction_c)); +} + +unsigned L4SysExperiment::calculateTimeout() { + // [instr] / [instr / s] = [s] + unsigned seconds = L4SYS_NUMINSTR / L4SYS_BOCHS_IPS; + // 1.1 (+10 percent) * 1000 ms/s * [s] + return 1100 * seconds; +} + bool L4SysExperiment::run() { Logger log("L4Sys", false); BPSingleListener bp(0, L4SYS_ADDRESS_SPACE); @@ -194,7 +214,6 @@ bool L4SysExperiment::run() { ofstream golden_run_file(L4SYS_CORRECT_OUTPUT); bp.setWatchInstructionPointer(L4SYS_FUNC_EXIT); - bp.setCounter(L4SYS_ITERATION_COUNT); simulator.addListener(&bp); BaseListener* ev = waitIOOrOther(true); if (ev == &bp) { @@ -279,19 +298,23 @@ bool L4SysExperiment::run() { struct stat teststruct; if (stat(L4SYS_STATE_FOLDER, &teststruct) == -1 || stat(L4SYS_CORRECT_OUTPUT, &teststruct) == -1) { log << "Important data missing - call \"prepare\" first." << endl; - simulator.terminate(1); + simulator.terminate(10); } - ifstream golden_run_file(L4SYS_CORRECT_OUTPUT); + ifstream golden_run_file(L4SYS_CORRECT_OUTPUT); - golden_run.reserve(teststruct.st_size); + if(!golden_run_file.good()) { + log << "Could not open file " << L4SYS_CORRECT_OUTPUT << endl; + simulator.terminate(20); + } + golden_run.reserve(teststruct.st_size); - golden_run.assign((istreambuf_iterator(golden_run_file)), - istreambuf_iterator()); + golden_run.assign((istreambuf_iterator(golden_run_file)), + istreambuf_iterator()); - golden_run_file.close(); + golden_run_file.close(); - //the generated output probably has a similar length - output.reserve(teststruct.st_size); + //the generated output probably has a similar length + output.reserve(teststruct.st_size); log << "restoring state" << endl; simulator.restore(L4SYS_STATE_FOLDER); @@ -339,11 +362,23 @@ bool L4SysExperiment::run() { // inject if (exp_type == param.msg.GPRFLIP) { + if(!param.msg.has_register_offset()) { + param.msg.set_resulttype(param.msg.UNKNOWN); + param.msg.set_resultdata( + simulator.getRegisterManager().getInstructionPointer()); + param.msg.set_output(sanitised(output.c_str())); + + stringstream ss; + ss << "Sent package did not contain the injection location (register offset)" << endl; + param.msg.set_details(ss.str()); + simulator.terminate(30); + } + int reg_offset = param.msg.register_offset(); RegisterManager& rm = simulator.getRegisterManager(); - Register *ebx = rm.getRegister(RID_EBX); - regdata_t data = ebx->getData(); + Register *reg_target = rm.getRegister(reg_offset - 1); + regdata_t data = reg_target->getData(); regdata_t newdata = data ^ (1 << bit_offset); - ebx->setData(newdata); + reg_target->setData(newdata); // do the logging in case everything worked out logInjection(log, param); @@ -353,8 +388,8 @@ bool L4SysExperiment::run() { // this is a twisted one // initial definitions - bxICacheEntry_c *cache_entry = simulator.getICacheEntry(); - unsigned length_in_bits = cache_entry->i->ilen() << 3; + bxInstruction_c *currInstr = simulator.getCurrentInstruction(); + unsigned length_in_bits = currInstr->ilen() << 3; // get the instruction in plain text in inject the error there // Note: we need to fetch some extra bytes into the array @@ -383,22 +418,13 @@ bool L4SysExperiment::run() { &bochs_instr); // inject it - // backup the current and insert the faulty instruction - bxInstruction_c old_instr; - memcpy(&old_instr, cache_entry->i, sizeof(bxInstruction_c)); - memcpy(cache_entry->i, &bochs_instr, sizeof(bxInstruction_c)); - - // execute the faulty instruction, then return - singleStep(); - - //restore the old instruction - memcpy(cache_entry->i, &old_instr, sizeof(bxInstruction_c)); + injectInstruction(currInstr, &bochs_instr); // do the logging logInjection(log, param); } else if (exp_type == param.msg.RATFLIP) { - bxICacheEntry_c *cache_entry = simulator.getCPUContext()->getICacheEntry(); - Udis86 udis(calculateInstructionAddress(), cache_entry->i->ilen(), injection_ip); + bxInstruction_c *currInstr = simulator.getCurrentInstruction(); + Udis86 udis(calculateInstructionAddress(), currInstr->ilen(), injection_ip); if (udis.fetchNextInstruction()) { ud_t _ud = udis.getCurrentState(); @@ -493,28 +519,50 @@ bool L4SysExperiment::run() { // restore the actual value of the register // in reality, it would never have been overwritten rm.getRegister(bochs_reg)->setData(data); + + // and log the injection + logInjection(log, param); } } } else if (exp_type == param.msg.ALUINSTR) { + BochsALUInstructions aluInstrObject(aluInstructions, aluInstructionsSize); + // find the closest ALU instruction after the current IP + + bxInstruction_c *currInstr = simulator.getCurrentInstruction(); + while(!aluInstrObject.isALUInstruction(currInstr)) { + singleStep(); + currInstr = simulator.getCurrentInstruction(); + } + // now exchange it with a random equivalent + bxInstruction_c newInstr = aluInstrObject.randomEquivalent(); + if(!memcmp(&newInstr, currInstr, sizeof(bxInstruction_c))) { + // something went wrong - exit experiment + param.msg.set_resulttype(param.msg.UNKNOWN); + param.msg.set_resultdata( + simulator.getRegisterManager().getInstructionPointer()); + param.msg.set_output(sanitised(output.c_str())); + + stringstream ss; + ss << "Did not hit an ALU instruction - correct the source code please!" << endl; + param.msg.set_details(ss.str()); + simulator.terminate(40); + } + // inject it + injectInstruction(currInstr, &newInstr); + + // do the logging + logInjection(log, param); } // aftermath BPSingleListener ev_done(L4SYS_FUNC_EXIT, L4SYS_ADDRESS_SPACE); - ev_done.setCounter(L4SYS_ITERATION_COUNT); simulator.addListener(&ev_done); - const unsigned instr_run = L4SYS_ITERATION_COUNT * L4SYS_NUMINSTR; - BPSingleListener ev_timeout(ANY_ADDR, L4SYS_ADDRESS_SPACE); - ev_timeout.setCounter(instr_run + 3000); + BPSingleListener ev_incomplete(ANY_ADDR, L4SYS_ADDRESS_SPACE); + ev_incomplete.setCounter(static_cast(L4SYS_NUMINSTR * 1.1)); + simulator.addListener(&ev_incomplete); + TimerListener ev_timeout(calculateTimeout()); simulator.addListener(&ev_timeout); - TrapListener ev_trap(ANY_TRAP); - //one trap for each 150 instructions justifies an exception - ev_trap.setCounter(instr_run / 150); - simulator.addListener(&ev_trap); - InterruptListener ev_intr(ANY_INTERRUPT); - //one interrupt for each 100 instructions justifies an exception (timeout mostly) - ev_intr.setCounter(instr_run / 100); - simulator.addListener(&ev_intr); //do not discard output recorded so far BaseListener *ev = waitIOOrOther(false); @@ -533,25 +581,18 @@ bool L4SysExperiment::run() { param.msg.set_resulttype(param.msg.WRONG); param.msg.set_output(sanitised(output.c_str())); } + } else if (ev == &ev_incomplete) { + log << dec << "Result INCOMPLETE" << endl; + param.msg.set_resulttype(param.msg.INCOMPLETE); + param.msg.set_resultdata( + simulator.getRegisterManager().getInstructionPointer()); + param.msg.set_output(sanitised(output.c_str())); } else if (ev == &ev_timeout) { - log << dec << "Result TIMEOUT" << endl; + log << hex << "Result TIMEOUT; Last INT #:" << endl; param.msg.set_resulttype(param.msg.TIMEOUT); param.msg.set_resultdata( simulator.getRegisterManager().getInstructionPointer()); param.msg.set_output(sanitised(output.c_str())); - } else if (ev == &ev_trap) { - log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl; - param.msg.set_resulttype(param.msg.TRAP); - param.msg.set_resultdata( - simulator.getRegisterManager().getInstructionPointer()); - param.msg.set_output(sanitised(output.c_str())); - } else if (ev == &ev_intr) { - log << hex << "Result INT FLOOD; Last INT #:" - << ev_intr.getTriggerNumber() << endl; - param.msg.set_resulttype(param.msg.INTR); - param.msg.set_resultdata( - simulator.getRegisterManager().getInstructionPointer()); - param.msg.set_output(sanitised(output.c_str())); } else { log << dec << "Result WTF?" << endl; param.msg.set_resulttype(param.msg.UNKNOWN); diff --git a/src/experiments/l4-sys/experiment.hpp b/src/experiments/l4-sys/experiment.hpp index 70d853aa..4eddc905 100644 --- a/src/experiments/l4-sys/experiment.hpp +++ b/src/experiments/l4-sys/experiment.hpp @@ -6,7 +6,6 @@ #include "efw/ExperimentFlow.hpp" #include "efw/JobClient.hpp" #include "util/Logger.hpp" -#include "aluinstr.hpp" class L4SysExperimentData; @@ -65,6 +64,16 @@ private: * Proceeds by one single instruction. */ void singleStep(); + /** + * Injects a new instruction into the Bochs instruction stream and restores the previous one + * @param oldInstr address of the instruction to be replaced + * @param newInstr address of the instruction to replace it with + */ + void injectInstruction(bxInstruction_c *oldInstr, bxInstruction_c *newInstr); + /** + * Calculate the timeout of the current workload in milliseconds. + */ + unsigned calculateTimeout(); }; #endif // __L4SYS_EXPERIMENT_HPP__ diff --git a/src/experiments/l4-sys/experimentInfo.hpp b/src/experiments/l4-sys/experimentInfo.hpp index 66e12e6d..86f8a318 100644 --- a/src/experiments/l4-sys/experimentInfo.hpp +++ b/src/experiments/l4-sys/experimentInfo.hpp @@ -4,14 +4,13 @@ // the maximum number of bytes in a Bochs instruction #define MAX_INSTR_BYTES 15 -// the bounds of the program +// the bounds of the program (space, instructions and time) #define L4SYS_ADDRESS_SPACE 0x203d000 #define L4SYS_FUNC_ENTRY 0x10025ca #define L4SYS_FUNC_EXIT 0x1002810 -#define L4SYS_NUMINSTR 83084798 -// kernel: 3599694, userland: 79485104 - -#define L4SYS_ITERATION_COUNT 1 +// kernel: 16300287, userland: 210785625 +#define L4SYS_NUMINSTR 227085912 +#define L4SYS_BOCHS_IPS 5000000 // several file names used #define L4SYS_STATE_FOLDER "l4sys.state" @@ -23,6 +22,6 @@ #define HEADLESS_EXPERIMENT // 0 - preparation complete // >0 - next step to execute -#define PREPARATION_STEP 3 +#define PREPARATION_STEP 0 #endif // __EXPERIMENT_INFO_HPP__ diff --git a/src/experiments/l4-sys/l4sys.proto b/src/experiments/l4-sys/l4sys.proto index 7bf124a5..c99f9215 100644 --- a/src/experiments/l4-sys/l4sys.proto +++ b/src/experiments/l4-sys/l4sys.proto @@ -11,24 +11,36 @@ message L4SysProtoMsg { required int32 instr_offset = 2; required int32 bit_offset = 3; + // registers + enum RegisterType { + EAX = 1; + ECX = 2; + EDX = 3; + EBX = 4; + ESP = 5; + EBP = 6; + ESI = 7; + EDI = 8; + } + optional RegisterType register_offset = 4; + // results // make these optional to reduce overhead for server->client communication enum ResultType { DONE = 1; - TIMEOUT = 2; - TRAP = 3; - INTR = 4; - WRONG = 5; - UNKNOWN = 6; + INCOMPLETE = 2; + TIMEOUT = 3; + WRONG = 4; + UNKNOWN = 5; } // instruction pointer where injection was done - optional uint32 injection_ip = 4; + optional uint32 injection_ip = 5; // result type, see above - optional ResultType resulttype = 5; + optional ResultType resulttype = 6; // result data, depending on resulttype (see source code) - optional uint32 resultdata = 6; + optional uint32 resultdata = 7; // generated output - optional string output = 7; + optional string output = 8; // optional textual description of what happened - optional string details = 8; + optional string details = 9; }