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_startafter setting bit 0 in CR0
Now everything seems to work although I'm wary of any other basic mistakes I might still be making.