Bochs Emulator – Debug & Instrument

 Bochs is an amazing thing because it provides instrumentation in the lowest level of the Operating System. One of the advantages of the Boch is being able to instrument in kernel-mode, which is not available in other instrumenting tools like Intel’s pin tool. You can see how to interact with Bochs debugger here. It’s somehow like Windbg in its syntax, if you enabled the debugger feature during the compilation then after running the OS, you can press ctrl+c and it gives you a command-line interface. In the rest of the post, I’m gonna explain instrumentation. Instrumenting in Bochs is depending on the following functions that exist in /stubs/ instrument.cc.

For using Bochs instrumentation, first, you need to configure Bochs with the following argument:

Then you can change the above file and compile your project again and run Bochs with its debugger feature then Bochs automatically sets your function as a callback to its main CPU emulation functions and every time, one of the above functions performed in the target machine, then you’ll be aware. The best reference for describing the above functions is Instrument.txt which exists under /instrument/Instrument.txt, I copied the newest version of Instrument.txt (at the time of writing this post), you can see the below file :

Instrumentation To use instrumentation features in bochs, you must compile in support for it. You should build a custom instrumentation library in a separate directory in the “instrument/” directory. To tell configure which instrumentation library you want to use, use the “–enable-instrumentation” option. The default library consists of a set of stubs, and the following are equivalent:

You could make a separate directory with your custom library, for example “instrument/myinstrument”, copy the contents of the “instrument/stubs” directory to it, then customize it. Use:

—————————————————————————– BOCHS instrumentation callbacks

The callback is called when Bochs is initialized, before of reading .bochsrc. It can be used for registration of parameters in siminterface. Then when bx_instr_init() is called it can access configuration parameters defined by bx_instr_init_env(), so instrumentalization module can use additional options in .bochsrc.

The callback is called each time Bochs exits.

The callback is called each time, when Bochs initializes the CPU object. It can be used for initialization of user’s data, dynamic memory allocation and etc.

The callback is called each time, when Bochs destructs the CPU object. It can be used for destruction of user’s data, allocated by bx_instr_init callback.

The callback is called each time, when Bochs resets the CPU object. It would be executed once at the start of simulation and each time that user presses RESET BUTTON on the simulator’s control panel.

The callback is called each time, when Bochs’ emulated CPU enters HALT or SHUTDOWN state.

The callback is called each time, when Bochs’ emulated CPU enters to the MWAIT state. The callback receives monitored memory range and MWAIT flags as a parameters.

The callback is called each time, when currently executed instruction is a conditional near branch and it is taken.

The callback is called each time, when currently executed instruction is a conditional near branch and it is not taken.

The callback is called each time, when currently executed instruction is an unconditional near branch (always taken).

The callback is called each time, when currently executed instruction is an unconditional far branch (always taken). Possible operation types, passed through bx_instr_ucnear_branch and bx_instr_far_branch are:

This callback is called right before Bochs executes a VMEXIT.

The callback is called each time, when Bochs completes to decode a new instruction. Through this callback function Bochs could provide an opcode of the instruction, opcode length and an execution mode (16/32/64). Note, that Bochs uses translation caches so each simulated instruction might be executed multiple times but decoded only once.

The callback is called each time, when Bochs simulator executes an interrupt (software interrupt, hardware interrupt or an exception).

The callback is called each time, when Bochs simulator executes an exception.

The callback is called each time, when Bochs simulator executes a hardware interrupt.

The callback is called each time the CLFLUSH instruction is executed.

The callback is called each time, when Bochs simulator executes a cache/tlb control instruction. Possible instruction types, passed through bx_instr_tlb_cntrl are:

The new_cr_value is provided for first 4 instruction types only and undefined for all others. Possible instruction types, passed through bx_instr_cache_cntrl are:

The callback is called each time, when Bochs simulator executes a PREFETCH instruction. Possible PREFETCH types:

The seg/offset arguments indicate the address of the requested prefetch.

This callback is called each time when WRMSR instruction is executed. MSR number and written value passed as parameters to the callback function.

The callback is called each time, when Bochs simulator starts a new repeat iteration.

The callback is called each time, when Bochs simulator starts a new instruction execution. In case of repeat instruction the callback will be called only once before the first iteration will be started.

The callback is called each time, when Bochs simulator finishes any instruction execution. In case of repeat instruction the callback will be called only once after all repeat iterations.

The callback is called each time, when Bochs simulator executes a linear memory access. Note that no page split accesses will be generated because Bochs splits page split accesses to two different memory accesses during its execution flow. The callback also will not be generated in case of direct physical memory access like page walks, SMM, VMM or SVM operations. Possible access types are: BX_READ, BX_WRITE and BX_RW. Currently the callback is not supported when repeat-speedups optimization is enabled.

The callback is called each time, when Bochs simulator executes a physical memory access. Physical accesses include memory accesses generated by the CPU during page walks, SMM, VMM or SVM operations. Note that no page split accesses will be generated because Bochs splits page split accesses to two different memory accesses during its execution flow. Possible access types are: BX_READ, BX_WRITE and BX_RW.

These callback functions are a feedback from various system devices. —————————————————————————– Known problems: 1. BX_INSTR_LIN_ACCESS doesn’t work when repeat-speedups feature is enabled. Feature requests: 1. BX_INSTR_CNEAR_BRANCH_NOT_TAKEN callback should have an additional ‘not taken’ new_rip parameter. 2. BX_INSTR_SMI, BX_INSTR_NMI, BX_INSTR_SIPI and other external events callbacks

  If you read the above description about instrument functions, then let’s have a look at some of the important ones! For debugging VMX you should use bx_instr_vmexit, but you should be sure to compile your Bochs with this feature enabled. By default it is enabled in the current version of Bochs :

bx_instr_phy_access can also help you debugging EPT (Extended Page Table) by checking physical addresses. There are also other functions like bx_instr_wrmsr which is used for detecting what kind of MSR indexes an operating system or system drivers try to use.

This post is written in cooperation with my friend sina

References

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.