Initor module is responsible for setting up the kernel environment before the main kernel's
HostEntry entry point is called. It uses the multiboot information table passed by the (unknown multiboot-compliant) bootloader, to locate the module files, and then calculate how much memory the kernel requires to start up. Based on that information, it finds a suitable continuous chunk of physical memory, solely for other kernel module segments, symbol tables, strings, and hash-tables. In that chunk, the module segments are loaded, they are relocated to the appropriate
Kernel Environment Memory
The Initor module calculates how much memory the kernel environment will require based on the following information:
It will add up the total memory image size of all kernel modules - based on the farthest address used in the kernel module to hold text or data. Each module is given 4-KB spacing to protect against any minor errors.
During init-time, the Initor module holds a global symbol table, which centrally holds all symbolic definitions, of all modules. This also prevents duplicate symbols from being linked separately (in C++ two weak symbols may be present in different modules, but the linker should choose only one).
A array of
KernelSymbol*buckets is kept, to make a hash-table. This is not generally used by other kernel-modules after init-time, because of the
ModuleLoadersubsystem in the
KernelHost, which then stores the symbol tables on its own later.
For the symbol tables, all string tables are copied into the kernel environment chunk. An array of
char*pointers is kept at the beginning, to hold pointers to string tables of the modules by their
orderIndex. So, the Initor module accesses symbol names in this part of the environment memory.
A array of
struct KernelModuleobjects is kept holding information about each kernel-module including the physical-base address, virtual-base address, symbol table, string table, etc.
The KernelHost (or main) module requires space for page-tables and a stack - so a AUTO_DAT memory buffer is also provided at the end - of size 32-KB which can be changed.
Although, the kernel-environment, as described above has enough information to easily load and relocate the kernel modules, the KernelHost requires support from the Initor module - to get a table of page-frame entries located at a 2-MB aligned address. So, at the closest 2-MB aligned address of the kernel-environment, the Initor module also allocates a MM_FRAME array - whose size is equal to the number of page-frames in physical memory.
Although the Initor module could know that
HostEntry is the entry of the KernelHost, it rather uses the ELF file of the first module to get the entry point of the kernel. As the KernelHost is listed as the first module in the
grub.cfg configuration file - the entry point of the KernelHost is called.
Signature of the entry point of the first kernel module
The Initor module passes arguments to the entry point of the main-module. It must be capable of using those arguments, to locate data structure, especially the AUTO_DAT buffer provided for allocating a new stack, and then copying those argument into the new stack.
unsigned (*callMain)(unsigned long multibootTable, unsigned long kernelBase, unsigned long kernelSize, struct KernelSymbol *globalSymbolTable, unsigned long globalSymbolCount, char **physStrtabByIndex, struct KernelSymbol **symbolBuckets, struct KernelModule *modInfoObjs);
unsigned long multibootTable- The physical address of the multiboot information table passed by the bootloader. This is usually unmodified.
unsigned long kernelBase- The physical address where the kernel environment has been loaded. The preferred address on the iA-PC architecture is the 16-MB mark above DMA memory. It can be anywhere - but is 99% 2-MB aligned (unless on low-memory devices where 2-MB aligning is impossible).
unsigned long kernelSize- The size of the kernel environment. This doesn't include the page-frame entries at the next 2-MB aligned address after the end of the kernel-environment (which is
kernelBase + kernelSize). Their address can be calculated easily - and their size is equal to the no. of page-frames in physical memory into the size of each page-frame entry - (see
sizeof_mmframein the page-frame allocator's header file).
struct KernelSymbol *globalSymbolTable- The physical address where the
Initormodule kept the
KernelSymbolobjects holding values and pointers to the symbol names (in physical memory).
unsigned long globalSymbolCount- The total no. of entries present in the symbol table passed as argument 4.
char **physStrtabByIndex- The
Initormodule copies the string tables after this array. This array (having as many entries as the number of kernel-modules loaded by the bootloader) holds the pointers to the string tables (in physical memory) of each kernel-module.
struct KernelSymbol **symbolBuckets- The KernelHost doesn't use this - but still, it holds any array of
KernelSymbol *pointers as buckets of an hash-table. Note that the no. of buckets is equal to the global symbol count.
struct KernelModule *modInfoObjs- An array of
struct KernelModuleobject holding information about each module. The count of modules is available in the multiboot-information table.
Porting the Initor to other hobbyist kernel environments
The booting process of the Silcos kernel is quite generic - you'll have to name your "main" module (the one which will get control first) the "KernelHost" (or modify the code in the Initor) in the
grub.cfg file. The multiboot information table will hold a entry for this module, whose first 10 letters should be "KernelHost" which identifies it as the "main" module.
Initor module uses a few headers like
<Multiboot2.h>, and some in
Utils. Import these in your kernel, so that the Initor can use them.
**Big fat note on C & C++ inter-usage in code: ** The Silcos Initor uses the
_CBUILD macro before including headers to inform you that it is a C file. If your kernel is built in C, either remove the C++ parts of the headers, or simply add a
#define _CBUILD at the first of all your C files.