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.
/* 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 1 MB.
/* Create empty page directory */
_init_setupPaging:
;(...)
/* 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 IDR and GDTR 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#
#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#
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.