This is a followup to my previous question about relocating my x86 bootloader. It seems I still don't understand x86 addressing schemes correctly, my code is now the following (after preprocessing):
.code16
.global _start
jmp _start
enable_interrupts:
sti
in $0x70, %al
and $0x7F, %al
out %al, $0x70
ret
disable_interrupts:
cli
in $0x70, %al
or $0x80, %al
out %al, $0x70
ret
a20_enable:
// removed for brevity
ret
.p2align 3
gdt_begin:
.quad 0
.word 0x000FFFFF & 0xFFFF, 0x0 & 0xFFFF; .byte (0x0 >> 16) & 0xFF, 0x80 | 0x10 | 0x08 | 0x02, 0xC0 | ((0x000FFFFF >> 16) & 0xF), (0x0 >> 24) & 0xFF
.word 0x000FFFFF & 0xFFFF, 0x0 & 0xFFFF; .byte (0x0 >> 16) & 0xFF, 0x80 | 0x10 | 0x02, 0xC0 | ((0x000FFFFF >> 16) & 0xF), (0x0 >> 24) & 0xFF
gdt_end:
gdt_descriptor:
.word gdt_end - gdt_begin
.long gdt_begin
protected_mode_enable:
lgdt gdt_descriptor
mov %cr0, %eax
or $1, %al
mov %eax, %cr0
ret
_start:
mov $0x60, %ax
mov %ax, %es
xor %bx, %bx
mov $DISK_BOOT_SECT_COUNT, %ah
mov $2, %al
mov $0, %ch
mov $1, %cl
xor %dh, %dh
int $0x13
reloc_jmp:
jmp $0x0, $reloc_done
reloc_done:
mov $0x60, %ax
mov %ax, %ds
mov %ax, %es
mov $0xFFFF, %bx
mov %bx, %ss
xor %ax, %ax
mov %ax, %sp
cld
call disable_interrupts
call a20_enable
test %ax, %ax
jz real_mode_boot_error
call protected_mode_enable
jmp $0x8, $protected_mode_start
real_mode_boot_error:
jmp real_mode_boot_error
.code32
protected_mode_start:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
mov $0x80000, %esp
I use the following linker script:
OUTPUT_FORMAT("elf32-i386");
ENTRY(_start);
SECTIONS
{
. = 0x600;
.text : {
out/boot_S.o(.text);
. = 510;
SHORT(0xAA55);
}
.data : SUBALIGN(2) {
*(.data);
*(.rodata*);
}
/DISCARD/ : {
*(.eh_frame);
*(.comment);
}
}
and create a binary image with:
gcc -m32 -fno-PIC -g -gdwarf -c asm/boot.S -o out/boot_S.o
ld -melf_i386 -Tout/boot.ld out/boot_S.o -o ../elf/boot.elf
objcopy -O binary ../elf/boot.elf ../img/boot.img
My code first loads the two disk sectors containing my bootloader to 0x600
, jumps there and then afterwards tries to enable protected mode. Everything works as expected until the jump to the 32 bit code segment starting at protected_mode_start
. objdump
disassembles that instruction as follows:
...
6ea: ea f1 06 08 00 ljmp $0x8,$0x6f1
000006ef <real_mode_boot_error>:
6ef: eb fe jmp 6ef <real_mode_boot_error>
000006f1 <protected_mode_start>:
6f1: 66 b8 10 00 8e d8 mov $0xd88e0010,%eax
...
So I would expected it to jump to 0x6f1
. But it doesn't, it jumps to 0xe05b
instead. Why? Is my GDT wrong? (it used to work before I added the relocation code). How do I fix this?
EDIT:
After reading all the comments to this question I have changed the following:
- fixed the GDT descriptor
- set DS/ES to 0x0 instead of 0x60 after relocation
- fixed the al/ah mixup
- don't perform any sort of jumps before the far jump to
protected_mode_start
after setting bit 0 in CR0
Now everything seems to work although I'm wary of any other basic mistakes I might still be making.