diff --git a/core/SAL/bochs/BochsController.cc b/core/SAL/bochs/BochsController.cc index ce650fba..e22ad5bb 100644 --- a/core/SAL/bochs/BochsController.cc +++ b/core/SAL/bochs/BochsController.cc @@ -117,6 +117,28 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp // implementation. } +void BochsController::onIOPortEvent(unsigned char data, unsigned port, bool out) { + // Check for active breakpoint-events: + fi::EventList::iterator it = m_EvList.begin(); + while(it != m_EvList.end()) + { + // FIXME: Maybe we need to improve the performance of this check. + fi::IOPortEvent* pIOPt = dynamic_cast(*it); + if(pIOPt && pIOPt->isMatching(port, out)) + { + pIOPt->setData(data); + it = m_EvList.makeActive(it); + // "it" has already been set to the next element (by calling + // makeActive()): + continue; // -> skip iterator increment + } + it++; + } + m_EvList.fireActiveEvents(); + // Note: SimulatorController::onBreakpointEvent will not be invoked in this + // implementation. +} + void BochsController::save(const string& path) { int stat; diff --git a/core/SAL/bochs/BochsController.hpp b/core/SAL/bochs/BochsController.hpp index 94b73356..f232c96b 100644 --- a/core/SAL/bochs/BochsController.hpp +++ b/core/SAL/bochs/BochsController.hpp @@ -86,9 +86,17 @@ class BochsController : public SimulatorController * Instruction pointer modification handler. This method is called (from * the Breakpoints aspect) every time when the Bochs-internal IP changes. * @param instrPtr the new instruction pointer - * @param address_space + * @param address_space the address space the CPU is currently in */ void onInstrPtrChanged(address_t instrPtr, address_t address_space); + /** + * I/O port communication handler. This method is called (from + * the IOPortCom aspect) every time when Bochs performs a port I/O operation. + * @param data the data transmitted + * @param port the port it was transmitted on + * @param out true if the I/O traffic has been outbound, false otherwise + */ + void onIOPortEvent(unsigned char data, unsigned port, bool out); /** * This method is called when an experiment flow adds a new event by * calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev). diff --git a/core/SAL/bochs/IOPortCom.ah b/core/SAL/bochs/IOPortCom.ah new file mode 100644 index 00000000..3c9e0296 --- /dev/null +++ b/core/SAL/bochs/IOPortCom.ah @@ -0,0 +1,38 @@ +#ifndef __IOPORT_COM_AH__ +#define __IOPORT_COM_AH__ + +#include "config/FailConfig.hpp" + +#ifdef CONFIG_EVENT_IOPORT + +#include "../../../bochs/bochs.h" +#include "../../../bochs/cpu/cpu.h" +#include "../SALInst.hpp" + +#include "bochs_helpers.hpp" + +aspect IOPortCom +{ +// ATM only capturing bytewise output (most common, I suppose) + pointcut outInstruction() = "% ...::bx_cpu_c::OUT_DXAL%(...)"; + + advice execution (outInstruction()) : after () + { + unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number + unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data + sal::simulator.onIOPortEvent(rAL, rDX, true); + } + + pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX%(...)"; + + advice execution (inInstruction()) : after () + { + unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number + unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data + sal::simulator.onIOPortEvent(rAL, rDX, false); + } +}; + +#endif // CONFIG_EVENT_IOPORT + +#endif /* __IOPORT_COM_AH__ */ diff --git a/core/config/CMakeLists.txt b/core/config/CMakeLists.txt index b757d3e6..742c8ae6 100644 --- a/core/config/CMakeLists.txt +++ b/core/config/CMakeLists.txt @@ -6,6 +6,7 @@ OPTION(CONFIG_EVENT_BREAKPOINTS "Event source: Breakpoints" OFF) OPTION(CONFIG_EVENT_MEMREAD "Event source: Memory reads" OFF) OPTION(CONFIG_EVENT_MEMWRITE "Event source: Memory writes" OFF) OPTION(CONFIG_EVENT_GUESTSYS "Event source: Outbound guest-system communication" OFF) +OPTION(CONFIG_EVENT_IOPORT "Event source: I/O port communication" OFF) OPTION(CONFIG_EVENT_INTERRUPT "Event source: Interrupts" OFF) OPTION(CONFIG_EVENT_TRAP "Event source: Traps" OFF) OPTION(CONFIG_EVENT_JUMP "Event source: Branch instructions" OFF) diff --git a/core/config/FailConfig.hpp.in b/core/config/FailConfig.hpp.in index c436c652..ae0dee9e 100644 --- a/core/config/FailConfig.hpp.in +++ b/core/config/FailConfig.hpp.in @@ -10,6 +10,7 @@ #cmakedefine CONFIG_EVENT_MEMREAD #cmakedefine CONFIG_EVENT_MEMWRITE #cmakedefine CONFIG_EVENT_GUESTSYS +#cmakedefine CONFIG_EVENT_IOPORT #cmakedefine CONFIG_EVENT_INTERRUPT #cmakedefine CONFIG_EVENT_TRAP #cmakedefine CONFIG_EVENT_JUMP diff --git a/core/controller/Event.hpp b/core/controller/Event.hpp index 3bfded85..7eb72b66 100644 --- a/core/controller/Event.hpp +++ b/core/controller/Event.hpp @@ -477,6 +477,60 @@ class GuestEvent : virtual public BaseEvent void setPort(unsigned port) { m_Port = port; } }; +/** + * \class IOPortEvent + * Observes I/O access on architectures with a separate I/O access mechanism (e.g. IA-32) + */ +class IOPortEvent : virtual public BaseEvent +{ + private: + unsigned char m_Data; + unsigned m_Port; + bool m_Out; + public: + /** + * Initialises an IOPortEvent + * + * @param port the port the event ist listening on + * @param out Defines the direction of the event. + * \arg \c true Output on the given port is captured. + * \arg \c false Input on the given port is captured. + */ + IOPortEvent(unsigned port, bool out) : m_Data(0), m_Port(port), m_Out(out) { } + /** + * Returns the data sent to the specified port + */ + unsigned char getData() const { return (m_Data); } + /** + * Sets the data which had been transmitted. + */ + void setData(unsigned char data) { m_Data = data; } + /** + * Retrieves the port which this event is bound to. + */ + unsigned getPort() const { return (m_Port); } + /** + * Sets the port which this event is bound to. + */ + void setPort(unsigned port) { m_Port = port; } + /** + * Checks whether a given port number is matching. + * @param p The port number an I/O event occured on + * @param out True if the communication was outbound, false otherwise + */ + bool isMatching(unsigned p, bool out) const { return ( out = isOutEvent() && p == getPort()); } + /** + * Tells you if this event is capturing outbound communication (inbound if false) + */ + bool isOutEvent() const { return m_Out; } + /** + * Change the event direction. + * \arg \c true Output on the given port is captured. + * \arg \c false Input on the given port is captured. + */ + void setOut(bool out) { m_Out = out; } +}; + /** * \class JumpEvent * JumpEvents are used to observe conditional jumps (if...else if...else). diff --git a/core/experiments/l4sys/experiment.cc b/core/experiments/l4sys/experiment.cc index 1b565207..5c8ea9d9 100644 --- a/core/experiments/l4sys/experiment.cc +++ b/core/experiments/l4sys/experiment.cc @@ -15,7 +15,7 @@ #include "SAL/Memory.hpp" #include "SAL/bochs/BochsRegister.hpp" #include "controller/Event.hpp" -#include "config/AspectConfig.hpp" +#include "config/FailConfig.hpp" #include "l4sys.pb.h" @@ -24,9 +24,9 @@ using std::endl; // Check if configuration dependencies are satisfied: #if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \ !defined(CONFIG_SR_SAVE) || !defined(CONFIG_SUPPRESS_INTERRUPTS) || \ - !defined(CONFIG_EVENT_TRAP) || !defined(CONFIG_EVENT_GUESTSYS) || \ + !defined(CONFIG_EVENT_TRAP) || !defined(CONFIG_EVENT_IOPORT) || \ !defined(CONFIG_EVENT_INTERRUPT) -#error This experiment needs: breakpoints, suppressed-interrupts, traps, guest system and interrupt events, \ +#error This experiment needs: breakpoints, suppressed-interrupts, traps, I/O port and interrupt events, \ save, and restore. Enable these in the configuration. #endif @@ -56,17 +56,17 @@ std::string L4SysExperiment::sanitised(std::string in_str) { return result; } -fi::BaseEvent* L4SysExperiment::waitGuestOrOther(bool clear_output) { - fi::GuestEvent ev_guest; +fi::BaseEvent* L4SysExperiment::waitIOOrOther(bool clear_output) { + fi::IOPortEvent ev_ioport(0x3F8, true); fi::BaseEvent* ev = NULL; if (clear_output) output.clear(); while (true) { - sal::simulator.addEvent(&ev_guest); + sal::simulator.addEvent(&ev_ioport); ev = sal::simulator.waitAny(); - sal::simulator.removeEvent(&ev_guest); - if (ev == &ev_guest) { - output += ev_guest.getData(); + sal::simulator.removeEvent(&ev_ioport); + if (ev == &ev_ioport) { + output += ev_ioport.getData(); } else { break; } @@ -149,7 +149,7 @@ bool L4SysExperiment::run() { bp.setWatchInstructionPointer(COOL_ECC_CALCDONE); bp.setCounter(times_run); sal::simulator.addEvent(&bp); - fi::BaseEvent* ev = waitGuestOrOther(true); + fi::BaseEvent* ev = waitIOOrOther(true); if (ev == &bp) { golden_run.assign(output.c_str()); golden_run_file << output.c_str(); @@ -206,7 +206,7 @@ bool L4SysExperiment::run() { bp.setWatchInstructionPointer(instr_list[instr_offset]); sal::simulator.addEvent(&bp); //and log the output - waitGuestOrOther(true); + waitIOOrOther(true); // inject sal::RegisterManager& rm = sal::simulator.getRegisterManager(); @@ -254,7 +254,7 @@ bool L4SysExperiment::run() { sal::simulator.addEvent(&ev_intr); //do not discard output recorded so far - fi::BaseEvent *ev = waitGuestOrOther(false); + fi::BaseEvent *ev = waitIOOrOther(false); /* copying a string object that contains control sequences * unfortunately does not work with the library I am using, diff --git a/core/experiments/l4sys/experiment.hpp b/core/experiments/l4sys/experiment.hpp index 6a8ea8aa..826a0e77 100644 --- a/core/experiments/l4sys/experiment.hpp +++ b/core/experiments/l4sys/experiment.hpp @@ -11,7 +11,7 @@ public: bool run(); private: std::string sanitised(std::string in_str); - fi::BaseEvent* waitGuestOrOther(bool clear_output); + fi::BaseEvent* waitIOOrOther(bool clear_output); }; #endif