QEMU In A Box

QEMU in Box

The objective of this report is to explain technical details about how we boxed QEMU to be a standard TLM-2.0 Initiator. This QEMU module can be instantiated in any TLM-2.0 simulation as will act as an initiator. The code can be obtained from subversion repository: https://svn.greensocs.com/public/packages/qemu_systemc/InABox

Wrapping QEMU

vl.c

QEMU is based on a endless loop that performs all steps necessaries for a proper system simulation. QEMU can't be used directly in a SystemC simulation because this endless loop doesn't yield and never gives control to SystemC simulation kernel. For that reason, we modify that main loop to periodically freezing QEMU execution and yielding to SystemC simulator kernel. This operation is done calling a wrapper method named check_wait, that compares the QEMU time with SystemC simulation time (Code 1). If these times differ, the wrapper will call sc_core::wait() for the amount of time that QEMU is ahead SystemC.

  1. static int main_loop(void)
  2. {
  3. ...
  4. // MARIUS
  5.   int64_t qemu_time_vm = qemu_get_clock(vm_clock);// /ticks_per_sec; ???
  6.   int64_t qemu_time_rt = qemu_get_clock(rt_clock);
  7.   check_wait(qemu_time_vm, ourWrapper);
  8. // MARIUS  
  9. ...
  10. }
Code 1: Call to wrapper method check_wait

sc_link.c

In other hand, as we did in previous unwrapped QEMU systemC module, there's a file in QEMU source tree that manages accesses to SystemC devices. Actually this file implements a QEMU device that register itself as a PCI device and captures all PCI accesses (read and write) to it and send them to the SystemC device. Sending the PCI accesses from QEMU to SystemC device means getting the address to read or write and the data to write and call the proper functions on the wrapper (code 2). Those functions (sc_read and sc_write) will perform the TLM-2.0 transaction through the socket to the TLM-2.0 Target device.

  1. static uint32_t sc_link_mem_readl(void *opaque, target_phys_addr_t addr) {
  2. ...
  3. val = sc_read(addr-reg_base, ourWrapper);
  4. ...
  5. }
  6.  
  7. static void sc_link_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) {
  8. ...
  9. sc_write(addr-reg_base, val, ourWrapper);
  10. ...
  11. }
Code 2: Call to wrapper methods to perform TLM-2.0 read/write transactions

No more major changes to QEMU source code are needed to allow it to be a TLM-2.0 module.

KVM

Seeing the modifications we did to QEMU, we think KVM should run with no problem in our modified QEMU. In our preliminary tests, KVM is running with no problem, but more extensive tests must be done.

Wrapper

We write a wrapper for QEMU to encapsulate all QEMU functionality into a TLM-2.0 Initiator. Actually the wrapper interface consists on a Master Greensocs GSGPSocket and a input interrupt line. In its constructor, the wrapper calls qemu_ini() function, passing as arguments: user parameters (int argc and char* argv[]), the callbacks functions to perform a read and a write to the GSSPSocket (sc_read and sc_write) and pointer to the wrapper itself. The wrapper has a SC_THREAD named run that just starts the QEMU main loop qemu_loop. Also, the wrapper has a SC_METHOD sensible to input IRQ line. Interrupt line changes will be sent to QEMU using sc_link functions sc_low_irq() and sc_raise_irq(). The wrapper also implements the sc_read() and sc_write() functions. These 2 functions are defined static to simplify the connection between C (QEMU) and C++ (SystemC and wrapper), because C cannot easily call C++ class instance functions. Those functions just pause QEMU execution and then calls a standard C++ method to perform the TLM-2.0 transaction (do_read() and do_write()) using the TLM-2.0 Initiator socket.

Compiling and using QEMU In Box

The Makefile provided with the package gives a complete compilation of the QEMU along with the wrapper and includes a simple TLM-2.0 simulation example. To use QEMU boxed with the wrapper in some other project, these files will be required:

  • sc_wrapper.c and sc_wrapper.h
  • libqemu.a
  • libqemu_common.a
  • libqemuSC.a

That last library needs to be build manually by now using the command in Code 3:

  1. cd i386-softmmu
  2. ar rcs libqemuSC.a vl.o ../osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.o virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o fw_cfg.o ../posix-aio-compat.o ../block-raw-posix.o lsi53c895a.o esp.o usb-ohci.o eeprom93xx.o eepro100.o ne2000.o pcnet.o rtl8139.o e1000.o msmouse.o ide.o pckbd.o ps2.o vga.o sb16.o es1370.o ac97.o dma.o fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o device-hotplug.o pci-hotplug.o gdbstub.o gdbstub-xml.o sc_link.o ../qemu-char.o
Code 3: Commands to build libqemuSC.a

Note that to have a complete simulation set-up using QEMU Boxed no source code from QEMU is needed, only the source for the wrapper and the binary libraries. The simple example and more information about compilation is at http://www.greensocs.com/en/Projects/QEMUSystemC/docs/ImportingWrapperforQEMUinInnovator

Add a new SystemC device to QEMU Boxed

In case the SystemC device emulates a present PCI device in QEMU, two major modifications are required: the sc_link.c file to enable this file to register itself as the device you are implementing in SystemC, the device itself. Changes in sc_link.c will consist in register the PCI device with the device configuration registers (PCI registers that tells PCI controller what kind of device is, its brand, fabric number, etc) and BAR regions (number of regions, type of accesses, size of memory, etc). Once done this step, next step will be register callbacks for any of the BAR registered regions. The main problem removing QEMU devices and getting them as SystemC devices outside the QEMU itself, is that original QEMU devices uses QEMU functions and structures for its functionality: i.e. a network device, uses QEMU functions to send and receive real packets from a real network. In case we would use those functionality in our SystemC devices, we need to implement a set of Back-end interfaces (using TLM-2.0 Sockets) to call those QEMU helper functions from our SystemC devices. Please, visit http://www.greensocs.com/en/Projects/QEMUSystemC/docs/QEMUSystemC/QEMUSystemCDataflow for a better explanation and diagrams.

More than one device in SystemC

In order to support more than one device outside QEMU and implemented in SystemC, we will need to modify our sc_link and wrapper to support it. The changes will be mainly to add a router (Greensocs greenrouter) to the sc_link. In this way, QEMU boxed will send TLM-2.0 transactions for the unique Initiator socket but targeting different Target devices. The management of the mapping between addresses and target sockets may be shared between sc_link and wrapper. More discussion and information about that topic is in: http://www.greensocs.com/en/Projects/QEMUSystemC/docs/QEMUSystemC/CompleteQEMUplatformusingSystemCmodules.