cs6465 Advanced OS - Lecture 10 - Bochs: Virtualization without jitting or direct execution
Watch on YouTube →
Overview
Anton Burtsev explains why Bochs, a software-only x86 interpreter, remains useful when hardware virtualization or JIT compilation is unavailable, and for deterministic whole-system tracing. The lecture details its instruction trace cache, branch-prediction-conscious call-site duplication, and lazy flag evaluation, describing roughly 1.9x improvement from software optimizations and up to 10x across software and hardware generations.
Key takeaways
- Bochs remains useful when VMX or AMD SVM is unavailable because it implements guest execution and system behavior in software, and its interpreter can run across different host platforms.
- Interpretation enables whole-system instrumentation, including recording taken branches, making Bochs useful for deterministic debugging and reproducible kernel-bug analysis.
- A trace cache speeds interpretation by retaining decoded instruction handlers and executing them repeatedly until control flow changes; the paper reports roughly a twofold gain from this approach.
- Interpreter dispatch can overload branch prediction when one indirect call site targets many instruction handlers; duplicating call sites can give different handlers more predictable branch targets.
- Lazy flag evaluation avoids computing every x86 arithmetic flag after each operation and instead derives needed flags from saved operands and results when a later instruction requests them.
- The paper reports about 1.9x improvement from software optimizations and up to 10x across software and hardware generations, showing that pure interpretation can be practical despite remaining slower than native execution.
Chapters
- Homework 2 follows the previous assignment's read-and-fill format, with a promised seven-day grace period for Homework 1.
- QEMU combines the KVM kernel module for VM entry and exits with user-space device emulation.
- Bochs provides full software emulation, making it useful for testing on Anton Burtsev's Mac and on an autograder with uncertain hardware.
- Bochs can instrument every guest instruction or taken branch because it controls instruction interpretation.
- For reproducible Linux kernel bugs, tracing from the triggering system call can expose execution details that are otherwise hard to diagnose.
- A hybrid workflow can use QEMU to reach a complex system state, then switch to interpretation to record execution.
- Bochs runs without VMX or AMD SVM, while direct execution depends on virtualization hardware and JIT compilation requires a translation system.
- A JIT must preserve precise guest state for exceptions, including the state associated with the exact instruction that faults.
- The paper argues for bounded memory and predictable costs, though Burtsev notes that memory limits matter less on modern machines.
- The paper argues that JIT performance depends on a warm translation cache large enough to retain a guest kernel's working set.
- Burtsev questions whether cache pressure remains a strong objection on modern CPUs with larger caches than the 64 KB L2 examples of older systems.
- Self-modifying code complicates translation, but Linux kernel alternatives and function-tracing trampolines are targeted mechanisms rather than browser-scale JIT workloads.
- Linux alternatives select implementation hooks at boot, such as choosing Xen-specific behavior versus native page-table operations.
- Function tracing can patch reserved no-op space in function prologues to record calls; eBPF adds a separate programmable kernel execution mechanism.
- Bochs offers deterministic behavior across host machines and can instrument whole-system execution, unlike tools such as Intel Pin that typically target user processes.
- Memory Protection Keys assign pages one of 16 protection-key values and use per-key permissions to restrict access within an address space.
- A browser-like process could isolate sensitive data, such as an SSH private key, from code running under a different key.
- Bochs can add MPK checks to its software address-translation logic, while a JIT without native MPK support may need checks inserted before memory accesses.
- The interpreter checks guest instruction access and privilege rules, fetches and decodes instructions, resolves operands, executes handlers, and commits state.
- A decoded-instruction cache can bypass repeated decoding and dispatch directly to the instruction's handler.
- Instrumentation for watchpoints, memory accesses, or branch traces can be inserted into the interpretation path.
- An x86 instruction's opcode identifies an operation such as a move, while ModR/M and SIB bytes encode operand forms.
- SIB means Scale, Index, Base; its fields combine registers and a scale factor to calculate an address such as base plus index times four.
- The interpreter decodes these variable instruction encodings to select the correct handler and determine the operands.
- Bochs's trace cache stores decoded instruction-handler references so repeated guest execution avoids decoding the same instructions again.
- The design resembles CPU micro-op caches, which retain decoded x86 operations rather than repeatedly translating instruction bytes.
- A trace is built through a basic block and stops at control-flow boundaries, since x86 instructions are variable length and indirect jumps make later decoding uncertain.
- Trace-cache entries are indexed by guest instruction address; a miss triggers decoding, while a hit lets execution run through cached handlers.
- Branches return execution to a cache lookup, and exceptions such as a page fault must leave the trace and be handled with precise guest state.
- Burtsev raises implementation questions about fall-through execution and exception exits, noting that the paper's description leaves some details unclear.
- The interpreter's shared dispatch call site invokes different instruction handlers through function pointers.
- A branch target buffer indexed by the call-site address can repeatedly predict the previous handler even when the next instruction needs a different one.
- The paper reports severe misprediction behavior on its tested Intel hardware, illustrating how interpreter dispatch can stress branch predictors.
- Bochs reduces dispatch conflicts by moving operand resolution into instruction-specific handlers, giving different handlers distinct call sites.
- The paper also duplicates register-form and memory-form logic to remove a branch that tests the operand mode.
- Burtsev recommends checking Linux perf counters or Intel VTune before applying this technique to other branch-heavy code.
- Instead of calculating all x86 arithmetic flags after every operation, Bochs records the operation, operands, and result.
- When a later instruction needs a flag, such as a conditional jump, the interpreter computes only the relevant condition from the saved state.
- This is valuable in software emulation because host arithmetic hardware does not automatically produce the guest's x86 flag state.
- The paper's comparison reports about 1.9x improvement from software optimizations and up to 10x across the tested software and hardware generations.
- Benchmarks show Bochs can be slower than QEMU on some operations, while particular dispatch cases can favor Bochs over the QEMU version tested.
- Software translation still has costs for operations such as floating-point multiplication and page-fault handling, but the results show interpretation need not be prohibitively slow.
- Burtsev demonstrates Fabrice Bellard's JSLinux, which runs an x86 Linux environment in a browser using JavaScript emulation.
- The demonstration shows that a desktop and applications can run in the emulator, although graphics interaction and disk-heavy tasks are noticeably slow.
- The closing point is that Bochs-style emulation is practical for portability, testing, research tracing, and running older software even when it cannot match native execution speed.
Transcript excerpt
0:33 Okay, guys. Time to begin start. So, uh homework two is out, right? Hopefully, I hope that everyone finished homework one. But, if you haven't, there's like a I I think there is a 7-day I didn't configure it. I promised you 7-day grace period, no penalty. Uh but, uh start on homework two. It's again, like I was promised, it's almost It's It's the same format. So, you just read and fill. And it kind of follows the design uh lecture which we had. So, any questions about it? All good? It actually assembles kind of like It's not as hard. Okay, and today I wanted to talk about
1:20 uh Box, right? So, essentially, because we're all using Box. I mean, I don't know if you noticed. I mean, maybe it wasn't clear. I know, it wasn't in the instructions. Right? So, uh we use two emulators. Uh one is QEMU, one is Box, right? We kind of understand how QEMU works. So, QEMU is what we are building in our homeworks, right? It just sits inside the Linux kernel, uh uses KVM uh kernel module, which implements this kind of VM run, VM launch, exits, and then uh and then I just use this uh user side component, which is QEMU, which actually emulates devices, very similar to how we do it in VM run, So, at least this is likely to be reasonably clear. And box is the thing which I use on Mac, right? To
2:09 test our homeworks. And we use on the autograder just because I'm not sure which architecture is provided by the autograder. Uh and so, essentially box is a full emulator. It does doesn't rely on any hardware support. It does everything in software. And this lecture is about box and uh
This is the start of the transcript. Save this video free for the whole transcript, searchable and time-coded.
In this course
Related notes
Summary, takeaways, and chapters were generated by AI from the video's transcript and may contain errors. The video belongs to its creator, Anton Burtsev.