Sunday 20 May 2012

Compiling Andromeda


Andromeda is an interesting project, but in order to see what we're up to, you might just want to look beyond the code and into the binary.
As of yet, there's not that much going on, but the mere fact that we've got the possibility to boot, handle key-presses and actually have scrolling text on our screens is already quite a feat, for first time kernel programmers like us. Just imagine that the stuff going on in the background is a thousand times more epic than what you can see on screen.
This article will guide you through the building sequence for the Andromeda kernel project. We will start of by getting our tool chain in place. We won't go into detail on how the tools work by them selves. That's what Google is for.
Once we've got our tool chain in place, we'll go into basic compiling. With that behind us, we'll look at the option for providing flags, and last but certainly not least, we'll go into booting the kernel.

Tool chain

To compile Andromeda, we will need the following:
  • gcc
  • nasm
  • GNU-Make
  • ld
  • git
The compiler, assembler, make and linker respectively. For the ones that don't know what the different tasks of those tools are, here's a brief summary:

GCC

The compiler compiles C code, and translates it into binary object files. The C compiler doesn't know about the layout of the final layout of the binary image. All it knows how to do is translate C statements into machine instructions.

NASM

The assembler takes loose human-readable machine instructions and translates them into machine-readable machine instructions. The output is surprisingly similar to that of a compiler.

Make

Make is the tool that is the conductor of this small orchestra. It tells the compiler or assembler to translate their code into binary instructions. Once this is done it tells the linker to put it all together.

LD

The linker is the pasting tool. Until this point our sources have been translated into several binary images, but none of them are executable, because they have references to other files, which need to be linked together first.

Git

Git, the stupid content tracker, is a quick and easy to use version keeping system, that's used by projects such as the Linux kernel and Android. This will be used to get our code, and maybe even do some work on it. Who knows?!

Compiling

In order to build the kernel, we'll be issuing the make command in the src directory of the repository. This command will read the Makefile and based on what we've got there it will determine how to compile which source file.
Make takes some arguments. One of them is "-s". This silences make, and keeps the terminal relatively clean, and easy to read. Only warnings, errors and verbose messages will show up in the terminal, keeping it easy to follow what's happening.
Another argument is the "-j n" option, in which n is the number of jobs with which you want to compile the kernel. Generally to optimize the compilation for your system, it is best practice to take the number of cores (if you have hyperthreading, you can use that) and double it.
For example I have a core 2 dual, with 2 cores, so my most optimized command is:
make -sj 4

Compiler flags

To activate or disable some functionality in the kernel, there is a possibility to add some flags to the compilation. The easiest way to do this is to put the flags into the flags variable you hand to make. To enable a feature, the -D flag can be used. To disable, the -U flag is to be used. So for example to compile the slab allocator into the kernel the following command can be used:
make FLAGS=-D\ SLAB
or
make FLAGS="-D SLAB"
Work is under way to building a kernel configuration editor, so not all flags will have to either be looked up or be remembered. For now, to find out which flags are available, key in make usage.

Booting

There's a couple of different ways to boot Andromeda. The first and easiest is through the make test command.
Make test builds the source tree (FLAGS variable works with this target as well) and then tries to run it in an Qemu/KVM environment. Qemu has a pretty convenient option to emulate grub, which we take advantage of here. For those that don't know what Qemu is, it is a virtual machine manager, and KVM means kernel-based virtual machine (the commands can pretty much be used interchangably).
Another way is to create a director at /media/loop and download a floppy image from github. First you build the kernel, according to the instructions above, and then the image is put into the src/scripts directory. Next we issue the ./updatefloppy.sh command, which will ask you for your sudo password (please install sudo for this, if you haven't already).
To recap, the options are:
make test
or
make
cd scripts
wget https://github.com/downloads/Andromeda-Kernel/andromeda/floppy.img
./updatefloppy.sh; kvm -fda floppy.img -m 16M

Thursday 3 May 2012

What is virtual memory?


Lately I have been working on the virtual memory system. Now most of you out there won’t know what virtual memory is, so here is a brief explanation.
Virtual memory is the mechanism used to make all tasks think they’re the only task running on that system at any given time, unless communication through memory is desired, in which the operating system makes that possible through a mechanism called shared memory.
That’s virtual memory in one full sentence. If you still don’t get it, that’s perfectly understandable. So here is an explanation which is a tiny bit longer. It explains the goals of virtual memory and explains different approaches to solving the very same problem.
When writing the code for any particular task on any system it’s a nuisance, to say the least, to have to keep the possible existence of other tasks on that system into account. The sole role of the operating system is to help other tasks run, and so what it does, is try to give the entire memory space, to each and every task.
While the whole memory space won’t be possible due to technical restrictions (the kernel itself must be somewhere in memory as well), quite a lot can be made available to user space (that’s where user tasks run).
One of the ways to get this all to work is to have each task ask the kernel for more memory each time they need it, while keeping all the data available to all the other processes. Sometimes, on architectures which don’t support memory protection, this is the only way to go forward.
Another way is to have the processor translate the virtual addresses into physical ones while the kernel concerns itself with the allocation of physical pages (a page is a chunk of memory of a predefined size). When a process isn’t allowed to access a particular physical page, it is simply not mapped to any virtual address at the moment that task is running (again, besides the kernel, but this region of memory has other ways to protect itself).
The advantage of the latter approach is that in this model it isn’t possible for one task to modify critical data or code in the other task. When it is desired to have two tasks sharing a region of space, that can be accomplished by mapping virtual pages in both tasks to the very same physical pages.