QEMU: Call a Custom Function from TCG

One of features that makes QEMU so flexible is an ability to call to a function in the middle of executing translated target code phase. QEMU calls it a helper function. include/exec/def-helper.h [Note: changed to a set of helper headers on May 28. change log] is where QEMU defines macros for creating helper functions for us. Basically, they are just ordinary functions with a wrapper.

Do you remember that target code is translated into TCG intermediate code, then it is translated into host code? A helper function is blended during translation from target code to TCG code. Therefore, you can expected to see helper functions appearing in translate.c file. Helper functions are there to aid target code translation. Some target architecture instructions are hard to be described by pure TCG code. Helper functions provide an access to high-level functionalities and libraries in a running host.

Quick jump

ARM’s Helper Function in Action

I show an example use of a helper function in ARM below. This is how ARM handles an exception in QEMU.

// target-arm/translate.c
static void gen_exception(int excp) {
    TCGv_i32 tmp = tcg_temp_new_i32(); 
    tcg_gen_movi_i32(tmp, excp);
    gen_helper_exception(cpu_env, tmp); 
    tcg_temp_free_i32(tmp); 
}
// target-arm/op_helper.c
// HELPER(exception) macro expands to gen_helper_exception
void HELPER(exception)(CPUARMState *env, uint32_t excp)
{
    CPUState *cs = CPU(arm_env_get_cpu(env));
    cs->exception_index = excp;
    cpu_loop_exit(cs);
}

If an exception occurs during an execution, it uses the inserted exception helper function to stop QEMU and hop out to handle the exception from outer perspective. You can notice that parameters being passed to the helper function are converted to TCG variables. An emulation is like a remote island. We have to pack info into parcels and float them to a helper function, so that the helper function can obtain info of what is going on there. [Need expand what needs to be put in
a TCGv container]
.

HELPER Function Calling Pattern

… 
TCGv_i32 tmp1 = tcg_temp_new_i32();
TCGv_i32 tmp2 = tcg_temp_new_i32();
… // Assign or do something with TCG
gen_helper_exception(tmp1, tmp2);
tcg_temp_free_i32(tmp1);
tcg_temp_free_i32(tmp2);
…

There are also variants of TCGv you can use. For more information, please see tcg/tcg.h.

TCGv_i32 tmp = tcg_temp_new_i32();
TCGv_i64 tmp = tcg_temp_new_i64();
TCGv_ptr tmp = tcg_temp_new_ptr();

Create a Helper Function

There are 2 steps to create a helper function

  1. Define a prototype in target-arch/helper.h

    Use DEF_HELPER_N from include/exec/def-helper.h.

    DEF_HELPER_N(func_name, return_t, param_t1, param_t2, …, param_tN)

    For ARM’s exception helper function, its prototype is

    DEF_HELPER_2(exception, void, env, i32)

    Types for return and parameters you can use only ones defined in include/exec/def-helper.h.

    For return types (see dh_retvar_)

    For parameter types (see dh_ctype_)

  2. Declare its definition in target-arch/op_helper.c

    Use HELPER macro to declare. For ARM’s exception helper function, its declaration is

    void HELPER(exception)(CPUARMState *env, uint32_t excp) { 
        CPUState *cs = CPU(arm_env_get_cpu(env));
        cs->exception_index = excp;
        cpu_loop_exit(cs);
    }

HELPER macro is expanded into gen_helper_. Therefore, if you want to use a helper function, you have to type as in ARM’s exception helper function example.

gen_helper_exception(cpu_env, tmp);

 

Hello World helper function example

I am going to have QEMU to print out “Hello World” at the end of each instruction.

Here is the first step in creating a helper function, DEF_HELPER_N.

// target-arm/helper.h
DEF_HELPER_0(hello_world, void)

Then, the 2nd step is defining it.

// target-arm/op_helper.c
void HELPER(hello_world)() {
    printf("Hello World\n")
}

Now, we are going to use it after every single instruction translation. This is happened inside gen_intermediate_code_internal(). It is an architecture dependent C code. Each architecture has its own version (Obviously! They have totally different Instruction Set Architecture). To proof that I do not call it during translation time. I am going to add another printf as our reference.

// target-arm/translate.c
 static inline void gen_intermediate_code_internal(ARMCPU *cpu, 
                            TranslationBlock *tb, bool search_pc)
{ 
    …
        num_insns ++;
        gen_helper_hello_world();
    } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end &&
        !cs->singlestep_enabled &&
        !singlestep &&
        dc->pc < next_page_start &&
        num_insns < max_insns);
    printf("We've done translated target->tcg code\n"); 
    

It is going to print “We’ve done” after finishing each translation phase first. After that, it will print “Hello World” during emulation. Let’s build it.

$ cd /path/to/qemu/..
$ mkdir build
$ cd build
$ ../qemu/configure --prefix=$PWD/../qemu-user-arm \
                    --target-list=arm-linux-user

Next, we need a dummy object file for the translator to work on something. Here is my blank.c

// blank.c
int main() {
    return 0;
}

I used bare-metal ARM C compiler from Mentor Graphics: Sourcery CodeBench Lite. I compile the C file with this command.

$ arm-none-gnueabi-gcc blank.c -o blank.c –Wl,-e_init

The “-Wl,-e_init” is used because gcc can’t find a proper entry to the object file. This forces the entry point of this object to be “_init” symbol.

Now we are ready to run it.

$ qemu-arm blank.o

I got this result on my terminal.

We've done translated target->tcg code
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault

It worked as we expected. Even “Hello World” precedes “We’ve done” in the source code, it run later. Please don’t mind about the segmentation fault. I did this on arm linux userspace. The baremetal ARM does not conform the OS ABI, so it barked at us.

7 thoughts on “QEMU: Call a Custom Function from TCG

  1. I believe this post is the most awesome and knit explanations about the TCG Helper functions. Thanks for your work :P. I want to know you and your works more haha LOL

    1. Glad you made it here, JaehyukLee, though this is kind of very specific topic. Not many trespasser around. I am still a student, and my study is about the low-level magic happening inside OS and CPU. This is a summary of what I played with QEMU.

      If you want to dig around QEMU internals, I recommend you to follow these trails:
      1. TCG documentation
      1.1. http://git.qemu.org/?p=qemu.git;a=blob_plain;f=tcg/README;hb=HEAD
      1.2. http://wiki.qemu.org/Documentation/TCG/frontend-ops
      2. Emulation cycle, file and function explanations http://lists.gnu.org/archive/html/qemu-devel/2011-04/pdfhC5rVdz7U8.pdf
      3. Stefan Hajnoczi – she works for Red hat and is a qemu contributor. Google her, and you will find many presentations and blog posts.

      1. Oh Thanks for your recommendation! I also study about the OS especially it’s security and emulated environment like QEMU or hypervisor. Hope we could be a good friend ! 🙂

    1. I am not sure if I could answer your question. I havn’t looked into the MMU part, but I have to do it soon. SURE! We can work things out.
      Is your mail jhl9***? I will give you my email in there.

Leave a comment