During the development of a small kernel, I stumbled across a strange problem when booting up application processors using the APIC.
As stated on OSDev and the Intel-Manual, the processor first enters Real-Mode, and my goal is to get it to operate in Protected-Mode. After setting up a small stack and enabling the "A20"-Line and far-jumping to my 32-bit code, i tried to clear eax
using xor eax, eax
for sanity purposes.
To my suprise, only the lower word of eax
got cleared, but the high word remained unchanged.
Funnily, if I simply do an xor ax, ax
, instead of xor eax, eax
, the register is cleared entirely.
Below is my code for bootstrapping an application processor using the APIC:
; Extern reference to the GDTR
section .data
extern g_gdtr
; Serves as a temporary stack used for flushing the cpu-pipeline
SMP_BOOT_STACK_SIZE equ 64
smp_boot_stack:
dq 0
dq 0
dq 0
dq 0
section .text
global __smp_ap_rm_entry
align 0x1000
[bits 16]
; Real-Mode Entry point for other processor cores (AP's)
; after a INIT-SIPI-SIPI was issued
__smp_ap_rm_entry:
cli
xor ax, ax
mov ds, ax
mov ss, ax
lea bp, [ds:smp_boot_stack + SMP_BOOT_STACK_SIZE - 1]
mov sp, bp
; Enable A20 line
sti
in al, 0x92
or al, 2
out 0x92, al
cli
lgdt [ds:g_gdtr]
; Enable Protected Mode
mov eax, cr0
or eax, 0x1
mov cr0, eax
; Far jump
push 0x8
push __smp_ap_pm_entry
retf
align 4
[bits 32]
__smp_ap_pm_entry:
mov ax, word 0x10
; Doing this two times is somehow necessary (wtf?)
mov es, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
mov ds, ax
xor eax, eax
int 3 ; To check for changed values in qemu
jmp $
Moreover, I have also tried to assign a 32-bit value into a register, e.g. mov eax, 0xDEADBEEF
, but only the BEEF
part remains.
Does anyone have any idea why this is not working?
As @sj95126 hinted, it seemed like I had loaded the wrong GDT. After creating and loading a GDT with 32-bit Segment-Descriptors, the problem was resolved.
If anyone is interested, below is the code for my new GDT used for switching from Real-Mode to Protected-Mode:
struct SegmentDescriptor32_s
{
u16 SegmentLimitLow;
u16 BaseAddressLow;
union
{
struct
{
u32 BaseAddressMiddle : 8;
u32 Type : 4;
u32 DescriptorType : 1;
u32 DescriptorPrivilegeLevel : 2;
u32 Present : 1;
u32 SegmentLimitHigh : 4;
u32 System : 1;
u32 LongMode : 1;
u32 DefaultBig : 1;
u32 Granularity : 1;
u32 BaseAddressHigh : 8;
};
u32 Flags;
};
} __attribute__((packed));
__attribute__((aligned(0x1000)))
static struct SegmentDescriptor32_s smp_ap_gdt[] =
{
{ /* Null-Selector */
.SegmentLimitLow = 0x0,
.BaseAddressLow = 0x0,
.Flags = 0x0
},
{ /* Flat Code */
.SegmentLimitLow = 0xFFFF,
.BaseAddressLow = 0x0000,
.Flags = 0x00CF9A00
},
{ /* Flat Data */
.SegmentLimitLow = 0xFFFF,
.BaseAddressLow = 0x0000,
.Flags = 0x008F9200,
},
{ /* TSS */
.SegmentLimitLow = 0x68,
.BaseAddressLow = 0x0000,
.Flags = 0x00CF8900
}
};