HAL for IA32 architecture is located in src/hal/ia32. This chapter presents some important implementations issues.

Loading process

To prevent mixing of 16-bit code with 32-bit code and to load programs into the memory the loader is used. Loader is executed when computer starts and it is able to load programs (stored in ELF) from a filesystem supported by boot firmware (e.g. UEFI) or from specified disk device (when BIOS is used).

All memory locations of kernel, programs, page directories, page tables, descriptor tables are stored in syspage_t structure passed to the kernel. When loader works in the real model and kernel and programs are loaded into memory loader prepares GDT and IDT tables and switches CPU into the protected mode. Control is passed to kernel initialization code located in _init.S file. When loader is executed in the protected mode kernel prepares new GDT and IDT tables, reloads GDTR and IDTR registers and after this initialization code is executed.


The brief analysis of initialization code is presented below.

movw $SEL_KDATA, %ax
movw %ax, %ss
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs

First instructions loads kernel data selector SEL_KDATA into segment registers. Kernel data selector points to descriptor in GDT defining the segment from address 0x00000000 to 0xffffffff on privilege level 0 with RW attributes.

/* Locate system page */
movl %esp, %eax
movl (%eax), %esi

Next instruction sequence copies the syspage_t address from the stack into esi register. This address will be stored after paging initialization in syspage variable.

/* Disable A20 line mask */
call _init_empty8042
movb $0xd1, %al
outb %al, $0x64
call _init_empty8042
movb $0xdf, %al
outb %al, $0x60
call _init_empty8042

Before enabling paging A20 line masking is disabled. Enabled A20 line masking prevents use of linear memory addresses greater than 1MB.

/* Create empty page directory */

/* Now enable paging */
movl %ecx, %cr3
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0

After disabling A20 line the page directory and page table are created and paging is enabled.

/* Relocate stack, GDT and IDT */
addl $VADDR_KERNEL, %esp
addl $2, %esi
addl $VADDR_KERNEL, (%esi)
addl $8, %esi
addl $VADDR_KERNEL, (%esi)
movl syspage, %eax
lgdt (%eax)
addl $8, %eax
lidt (%eax)

After enabling paging IDTR and GDTR registers are reload with relocated values.

/* Now jump to main function */
lea main, %eax
pushl %eax

The last part of code passes control to main() function.

Syspage definition

#pragma pack(1)

typedef struct _syspage_t {
  u8 gdtr[8];
  u8 idtr[8];
  u32 pdir;
  u32 ptable;
  u32 stack;
  u32 stacksize;
  u32 kernel;
  u32 kernelsize;
  char arg[256];
  u16 mmsize;
  syspage_mmitem_t mm[SIZE_SYSPAGE_MM];
} syspage_t;

typedef struct {
  u32 addr;
  u32 reserved0;
  u32 len;
  u32 reserved1;
  u16 attr;
  u16 reserved2;
} syspage_mmitem_t;

#pragma pack(4)


Spinlocks are implemented using xchg instruction. The locking function has been presented below.

static inline void hal_spinlockSet(hal_spinlock_t *spinlock)
  __asm__ volatile
  (" \
    pushf; \
    popl %%ebx; \
    cli; \
1: \
    xorl %%eax, %%eax; \
    xchgl %1, %%eax; \
    cmp $0, %%eax; \
    jz 1b; \
    movl %%ebx, %0"
  : "m" (spinlock->eflags), "m" (spinlock->lock)
  : "eax", "ebx", "memory");

First instruction stores flags on the stack.

Context switching

The context for IA32 has been presented below.

typedef struct {
    u32 savesp;
    u32 edi;
    u32 esi;
    u32 ebp;
    u32 edx;
    u32 ecx;
    u32 ebx;
    u32 eax;
    u16 gs;
    u16 fs;
    u16 es;
    u16 ds;

    /* eip, cs, eflags, esp, ss saved by CPU on interrupt */
    u32 eip;
    u32 cs;
    u32 eflags;
    u32 esp;
    u32 ss;
} cpu_context_t;

First part of the context is stored on the kernel stack automatically by CPU. After this part the general purpose registers are stored. On top of the stack is pushed the stack pointer for context switching.