HAL for IA32 based targets¶
HAL for IA32 architecture is located in hal/ia32. This chapter presents some important implementation issues.
Initialization¶
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.
/* Create empty page directory */
_init_setupPaging:
;(...)
/* Now enable paging */
movl %cr0, %eax
orl $PAGING_ENABLE, %eax
movl %eax, %cr0
Then the page directory and page table are created and paging is enabled.
/* Relocate stack */
addl $VADDR_KERNEL, %esp
pushl %esi
lea _hal_configInit, %eax
call *%eax
addl $4, %esp
After enabling paging IDT and GDT registers are reloaded with relocated values.
/* Now jump to main function */
lea main, %eax
pushl %eax
ret
The last part of code passes control to main() function.
Syspage definition¶
typedef struct {
struct {
unsigned short size;
unsigned int addr;
} __attribute__((packed)) gdtr;
unsigned short pad1;
struct {
unsigned short size;
unsigned int addr;
} __attribute__((packed)) idtr;
unsigned short pad2;
unsigned int pdir;
unsigned int ptable;
unsigned int stack;
unsigned int stacksz;
unsigned int ebda;
unsigned int acpi_version;
unsigned int localApicAddr;
unsigned long madt; /* addr_t */
unsigned int madtLength;
unsigned long fadt; /* addr_t */
unsigned int fadtLength;
unsigned long hpet; /* addr_t */
unsigned int hpetLength;
unsigned long mcfg; /* addr_t */
unsigned int mcfgLength;
struct {
unsigned short width;
unsigned short height;
unsigned short bpp;
unsigned short pitch;
unsigned long framebuffer; /* addr_t */
} __attribute__((packed)) graphmode; /* Graphics mode info */
} __attribute__((packed)) hal_syspage_t;
Spinlocks¶
Spinlocks are implemented using xchg instruction. The locking function has been presented below.
void hal_spinlockSet(spinlock_t *spinlock, spinlock_ctx_t *sc)
{
__asm__ volatile (
"pushf\n\t"
"popl %%ebx\n\t"
"cli\n"
"1:\n\t"
"xorl %%eax, %%eax\n\t"
"xchgl %1, %%eax\n\t"
"testl %%eax, %%eax\n\t"
"jz 1b\n\t"
"movl %%ebx, (%0)"
:
: "r" (sc), "m" (spinlock->lock)
: "eax", "ebx", "memory");
hal_cpuGetCycles((void *)&spinlock->b);
}
First instruction stores flags on the stack.
Context switching¶
The context for IA32 has been presented below.
/* CPU context saved by interrupt handlers on thread kernel stack */
typedef struct _cpu_context_t {
u32 savesp;
u32 edi;
u32 esi;
u32 ebp;
u32 edx;
u32 ecx;
u32 ebx;
u32 eax;
u16 gs;
u16 fs;
u16 es;
u16 ds;
fpu_context_t fpuContext;
u32 cr0Bits;
u32 eip; /* eip, cs, eflags, esp, ss saved by CPU on interrupt */
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.