Porting a software to another platform is not as easy as it sounds. For those who are unfamiliar with what porting means, think of how you can’t run programs that run on Windows directly on a Mac. (i.e. You can’t run a program with a .exe extension on a Mac or one with a .dmg extension on a Windows machine) This is because there are parts of the software that are dependent on the platform they run on. For example, even creating a simple thread differs widely among different kernels. For example, under Windows NT (the kernel that modern Windows OSs like Windows 7, Windows 8, etc. uses), CreateThread function is used to create a new thread, whereas in Linux kernel, kthread_create() can be used, which is defined in <linux/thread.h>
As for that reason, the Palacios VMM cannot directly run on Nautilus kernel as of now. However, it is possible to port Palacios to Nautilus, because Palacios is designed to be embeddable to different OSs, and has already been ported across numerous platforms, such as Linux kernel and the Kitten Lightweight kernel.
The way this works is shown in this rather mediocre diagram I drew:
All the functions that are defined in the Palacios source code can be compiled into a static library, called libv3vee.a. Nautilus can be then re-compiled to include this library when it is built, so that all the relevant VMM functionality is embedded into the kernel. The interaction with the guest OS can be supported by the definitions provided by the libv3vee library. libv3vee itself is about 8MB in size, and combined with Nautilus it provides a nice 20MB image, which is not an impossible size to be embedded into a ROM.
Palacios needs a defined set of OS hooks to work properly. The code below shows this:
This struct defines the set of hooks that it needs to run the guest kernel. Most of these come down to functionalities relevant to memory and page allocation, CPU and process controls for multiprocessing, thread and locks. The functions on the right that begin with prefix palacios_ are functions that I had to write, most of which are just direct function calls that Nautilus provides. There was a slight complication with the memory relevant functions, which I will have to explain in another post.
Some of these functions were not provided by the Nautilus kernel, such as starting a thread that already has been created (start_thread). To provide this, I had to extend the Nautilus kernel’s thread implementation to support this. It wasn’t too much work – I just added a way to associate a thread id with the information about that thread like the function to execute and its input arguments. After that, running a thread came down to just 3 lines of code, which looks something like this:
int nk_thread_run (nk_thread_id_t t) {
nk_thread_t * tid = (nk_thread_t*)t;
// this sets up the stack before running it
thread_setup_init_stack(tid, tid->fun, tid->input);
// this puts the thread on the run queue of cpu
nk_enqueue_thread_on_runq(tid, tid->bound_cpu);
return 0;
}
If only it worked after just doing these, my project would be almost over, and I would’ve posted many more blog posts for the past few weeks I have been silent (…), but life is never that easy in systems programming, and lots of things have been broken and fixed, some remaining broken.
I’ll be posting about page allocator I had to write and a bizarre bug that was solved but I still cannot figure out why the fix solved the problem, possibly tomorrow.