eyalz800 / zpp_hypervisor

A very simple hypervisor for learning experience.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Some questions about the loader and how it calls into hypervisor

hetong07 opened this issue · comments

Hello Eyal,

Your hypervisor is very interesting, and I learnt a lot from it. However, I have some questions about the loader and how to calls into hypervisor's main() function.

 for (std::size_t i{}; i < cpus; ++i) {
        // The launch function.
        // hetong07: Here only define the lambda function but not actually call it.
        auto launch = [&] {
            if (adjust_launch_calling_convention) {
                return adjust_launch_calling_convention(
                    entry, i, physical_to_virtual);
            }
            return entry(i, physical_to_virtual);
        };

        // The erased launch function.
        auto erased_launch = [](void * context) {
            auto & local_launch =
                *static_cast<decltype(launch) *>(context);
            return local_launch();
        };

        // Call on specified CPU.
        auto result =
            call_on_cpu(i,
                        static_cast<int (*)(void *)>(erased_launch),
                        std::addressof(launch))

Since we already have the elf's entry, why wrap them in two lambda function instead of calling the entry directly?

zpp::error hypervisor::main(x64::context & caller_context)
{
    ...
}

And in the loader, it jumps here through entry() function call. But why the hypervisor could access the parameters using x64::context & caller_context? I guess that since launch lambda function directly calls the entry() function, all the registers are saved in its stack frame, so by accessing the launch lambda function's stack frame we can access all those registers contents. But if my guess is correct, how could the caller_context points to the launch lambda function's stack frame? stack frame is retrieved from esp and ebp, and both register cannot be accessed in the C/C++ program?

Hope you can spare some time to answer my questions. Thank you.

Hi,

Thanks for being interested in this work.
Regarding your first question of why use 2 lambdas - the answer is really because the first lambda is capturing some variables and would not be convertible to a normal function pointer that is needed for the call_on_cpu. I can elaborate why if you are interested. The erased lambda however is not capturing any variable but assumes it receives the first lambda object as parameter. So we just convert the erased lambda to a function pointer and send to call_on_cpu and for the parameter we send the address of the lambda object to be sent to the erased lambda. Hope this answers it.

Next is I believe a misunderstanding of the flow a bit. The function that gets called in the hypervisor is the entry point of the elf, but it is not the hypervisor::main, it is the _start function in main.cpp of the hypervisor project - it is written in assembly, go check it out and see if it is clearer.

@eyalz800 Thank you very much for your timely reply, and thank you for you explanation. :)