Ace3\src\kernel\i386\start.asm
Ace3\src\kernel\main.c
Ace3\src\kernel\i386\arch.c

Ace
kernel depends on a multi-boot loader to boot. http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
http://www.gnu.org/software/grub/manual/grub.html
After GRUB loads an operating
system, control is passed to the kernel. This is accomplished by using the
kernel entry point field in the multi-boot structure. Since this entry will be
coded in assembly, each architecture will contain its
own entry point in its directory. The current implementation is for i386 and
the kernel entry is defined in Ace3\src\kernel\i386\start.asm file.
I386 kernel_entry function creates
kernel stack (move the esp to correct position) and
setups the initial page table and calls the _cmain function.
cmain() is the first c
function which will called in the booting process. It is an architecture
independent function and defined in Ace3\src\kernel\main.c. The name cmain() instead of main() because in PE executable for
defining main() requires definition of __main() function also. cmain() uses virtual addresses not physical addresses that
is cmain() expects some virtual memory support from the boot loader and/or
kernel entry although vm subsystem is not yet initialized.
cmain() expects the system to be
in virtual memory(paged) mode because it will access all the global variables
using virtual address(above 3gb) and not using physical address. So
_KernelEntry should set up this environment before calling cmain().
In i386 implementation, InitKernelPageDirectory() is
called by KernelEntry to switch to paged mode.
The other entries
maps the kernel code and data. One more PTE is created for purpose of
self mapping.
Called by cmain()
during boot and defined inside $(ARCH) folder. This function is responsible for
initializing the architecture depended code. Note: this part is not responsible
for initializing generic VM subsystem; however it is responsible anything that
generic VM is depended.
Since currently only i386 is
supported, it is discussed next.
It consists of the following
steps.
1) Install GDT(Global
Descriptor table) and IDT(Interrupt descriptor table)
2) Detect Resources(available
memory, CPUs, PIC etc)
3) Initialize PM(physical
memory)
4) Enable Paging
GDT
In order to protect or differentiate
code, data, stack and other segments in memory protected segmented memory
model is introduced in i386 (protected mode).
A program is composed of different
segments: Text, Data, Stack, etc
.. In real mode there is no way to protect
data segment from being executed and there is no way to protect text segment
being overwritten. In protected mode
these can be achieved using different segments and setting appropriate
permission flags. These segments are stored in a table called GDT- Global Descriptor table.
However for some reasons almost
all i386 kernels dont use the segmented model. Ace kernel also ignores the
segmented memory model. Although the kernel does not use segmented model, i386
processor expects at least one segment to be defined in the GDT. So the kernel
creates 4 entries in the beginning.
1) kernel
code
2) kernel
data
3) user
code
4) user
data
All the entries points to the same
flat segment 0 to 4GB. However the permission bit will be differ. Since the
GDT is a fixed table it is defined as static array. See i386/gdt.c
IDT
Processor is responsible for invoking
appropriate kernel functions when an interrupt or exception occurs. The
processor uses the IDT(interrupt descriptor table) to load the
kernel functions. The kernel is responsible for setting up these entry points
during the boot. See i386/idt.c and
i386/idt_stub.asm
The first 32 entry in this table
is reserved for exceptions (faults, traps and aborts). Ace kernel contains all
its exception entry points in i386/exception.c. Currently the implementation is
just to print the state of the machine and halt.
The remaining entries in the IDT
table are used for external hardware interrupts and software interrupts.
External hardware interrupts are
delivered by PIC - Programmable Interrupt
controller to the processor. PIC designed to deliver interrupt at vector
0-7 which is incorrect in protected because the first 32 entries are used for
exceptions. So the PIC has to be reprogrammed and it is done in
i386/interrupts.c. This part will be rewritten to handle APIC.
Detecting resources at the early
stage is required because otherwise it is impossible to continue to initialize
the other structures. For example, if the available memory and memory range is known
then the system can initialize the pm subsystem.
Unlike exceptions, the entry
points of IDT are slightly different. The IDT entries from 32 to 48 are entry
into InterruptHandler(). This InterruptHandler has its
own table for interrupt service routine selection. This design is because to
capture the machine state before calling the ISR. This is mainly for debugging
purpose and this behaviour must modified during design
of first ISR.
During boot kernel modules should
loaded into the kernel address space. The kernel modules are loaded from
disk/network to memory by boot loader and loaded address is given to kernel. To
avoid dependency on different methods bootloaders(grub,
efi..), Ace needs all module should be loaded as a single file.
The file format is

FileHeader:
MagicNumber This is the first
field in the file and it identifies the file as Ace kernel module holder.
0xACE
TotalModules
Total number of modules present in the file. This field should be greater than
0.
ModuleHeader:
ModuleName Name of the module(max 30 characters)
Offset The starting offset of
the module contents from the file start.
Size Size of the module
ModuleContents:
Contains binary content
of the module.
The following functions are used
to load the kernel module.
LoadBootModuleContainer() To initialize and
load module container
LoadBootModule(char * module_name)
To load a kernel module into kernel memory.
InitBootModule(void * kern_addr) To
start a already loaded kernel module.