To maintain order, all files created with the use of make, that is binaries, object files, natively executed helper programs, etc. get placed in build/. Our project contains 2 Makefiles: one in it's root directory and one in build/. The reason is that it is easier to use Makefile to simply, elegantly and efficiently produce files in the same directory where it is. To produce files in directory other than Makefile's own, it requires this directory to be specified in many rules across the Makefile and in general it complicates things. Also, a problem arises when trying to link objects not from within the current directory. If an object is referenced by name in linker script (which is a frequent practice in our scripts) and is passed to gcc with a path, then it'd need to also appear with that path in the linker script. Because of that a Makefile in build/ is present, that produces files into it's own directory and the Makefile in project's root is used as a proxy to that first one - it calls make recursively in build/ with the same target it was called with. These changes makes it easier to read. From now on only Makefile in build/ will be discussed. In the Makefile, variables with the names of certain tools and their command line flags are defined (using =? assignment, which allows one to specify their own value of that variable on the command line). In case a cross-compiler with a different triple should be used, ARM_BASE, normally set to arm-none-eabi, can be set to something like arm-linux-gnueabi or even /usr/local/bin/arm-none-eabi. All variables discussed below are defined using := assignment, which causes them to only be evaluated once instead of on every reference to them. Objects that should be linked together to create each of the .elf files are listed in their respective variables. I.e. objects to be used for creating kernel_stage2.elf are all listed in KERNEL_STAGE2_OBJECTS. When adding a new source file to the kernel, it is enough to add it's respective .o file to that list to make it compile and link properly. No other Makefile modifications are needed. In a similar fashion, RAMFS_FILES variable specifies files, that should be put in the ramfs image, that will be embedded in the kernel. Adding another file only requires listing it there. However, if the file is to be found somewhere else that build/, it might be useful to use the vpath directive to tell make where to look for it. Variables dirs and dirs_colon are defined to store list of all directories within src/, separated with spaces and colons, respectively. dirs_colons are used for vpath directive. 'dirs' variable is used in ARM_FLAGS to pass all the directories as include search paths to gcc. empty and space are helper variables - defining dirs_colon could be achieved without them (but it's clearer this way). The vpath directive tells make to look for assembler sources, C sources and linker scripts in all direct and indirect subdirectories of src/ (including itself). All other files shall be found/created in build/. The default target is the binary image of the kernel. The generic rule for compiling C sources uses cross-compiler or native compiler with appropriate flags depending on whether the source file is located somewhere under arm/ directory (which lies in src/) or enywhere else. The generic rules for making a stripped binary image out of elf file, for assembling an assembly file, for making an arbitrary file a linkable object and for linking objects are ARM-only. In C world it is possible to embed a file in an executable by using objcopy to create an object file from it and then linking that object file into the executable. In this project, at the current time, this is used only for embedding ramfs in the kernel (incbin is used for embedding kernel and loader second stages in their first stages). Generic rule for making a binary image into object file is present, in case it is needed somewhere else again. To link elf files, the generic rule is combined with a rule that specifies the elf's objects. Objects are listed in variables whenever more than one of them is needed. At this point in the Makefile, the dependence of objects created from assembly on files referenced in the assembly source via incbin is marked. Simple ram filesystem is created from files it should contain with the use of our own simple tool - makefs. Another 2 rules specify how native programs (for the machine we're working on) are to be linked. Rule qemu-elf runs the kernel in qemu emulating RaspberryPi 2 with 256MiB of memory by passing the elf file of the kernel to the emulator. Rule qemu-bin does the same, but passes the binary image of the kernel to qemu. Rule qemu-loader does the same, but first passes the binary image of the bootloader to qemu and the actual kernel is piped to qemu's standard input, received by bootloader as uart data and run. This method currently makes it impossible to pass any keyboard input to kernel once it's running. Rule run-on-rpi pipes the kernel through uart, assuming it is available under /dev/ttyUSB0, and then opens a screen session on that interface. This allows for executing the kernel on the Pi connected through UART, provided that our bootloader is running on the board. Rule clean removes all the files generated in build/. Rules that don't generate files are marked as PHONY.