It appears you are confused about segment:offset addressing.If you want to place your kernel at 0x1000:0x0000 (which is physical address 0x10000 (0x1000<<4+0x0000), the disk read needs to set ES to 0x1000 and BX to 0. In your linkboot1.ld you need to set the VMA to 0x0000(not 0x1000). LD doesn't know anything about segment:offset addressing. It just generates code relative an origin point. The origin point is the offset portion of 0x1000:0x0000, thus you need to set it 0x0000. Change
. = 0x1000; to
. = 0x0000;. In boot1.S you need to set the segments to 0x1000 using something like:
Code:
mov $0x1000, %ax
mov %ax, %ds
.You could also set ES to 0x1000 as well, but it doesn't appear ES is used in boot1.s.
Although not a problem in boot0.S you do
ljmp $0, $(boot0-init0+0x7c00). That could be simplified to
ljmp $0, $boot0.In boot1.S your GDT needs to use the linear address of the GDT. You have tomanually adjust the GDT address to account for the fact that it is loaded using segment 0x1000. You should be using something like
.long 0x1000<<4+gdt_start instead of
.long gdt_startYour code doesn't jump into protected mode after setting the protected mode bit. Because of the way that GNU assembler processes labels you will need to actually place the GDT before the FAR JMP so that the assembler realizes that the CODE_SEG declaration is in fact absolute. You can place the GDT and GDTR into the .data section before the .text section. You will also need to use ljmpl instead of jmpl because the address of the 32-bit code is in an offset >=64 kb. You will also need to compute the linear address of the 32-bit entry point by adding 0x1000<<4 to the address. Your boot1.S could look like:
Code:
.code16
.global init1
.set BOOT1_SEG, 0x1000
.set PM_STACK, 0x9c000
.section .data
gdt_start:
gdt_null:
.long 0
.long 0
gdt_code:
.word 0xffff
.word 0x0
.byte 0x0
.byte 0b10011010
.byte 0b11001111
.byte 0x0
gdt_data:
.word 0xffff
.word 0x0
.byte 0x0
.byte 0b10010010
.byte 0b11001111
.byte 0x0
gdt_end:
.set CODE_SEG, gdt_code - gdt_start
.set DATA_SEG, gdt_data - gdt_start
gdt:
.word (gdt_end - gdt_start - 1)
.long BOOT1_SEG << 4 + gdt_start
.boot1A20HasAlreadyBeenEnabled: .asciz "A20 Line has already been enabled"
.boot1A20HasBeenEnabled: .asciz "A20 Line Enabled"
.boot1LoadedMessage: .asciz "Entered 2nd Stage"
.boot1LoadedGDTMessage: .asciz "GDT Loaded"
.section .text
init1:
mov $BOOT1_SEG, %ax
mov %ax, %ds
mov $0x2, %ah
mov $0x1, %dh
mov $0x0, %dl
mov $0x0, %bh
int $0x10
call boot1LoadedMessage
mov $0x2, %ah
mov $0x2, %dh
mov $0x0, %dl
mov $0x0, %bh
int $0x10
call boot1EnableA20
mov $0x2, %ah
mov $0x3, %dh
mov $0x0, %dl
mov $0x0, %bh
int $0x10
call boot1LoadGDT
cli
mov %cr0, %eax
or $1, %eax
mov %eax, %cr0
ljmpl $CODE_SEG, $(BOOT1_SEG << 4 + start_32)
boot1EnableA20:
call boot1CheckA20LineState # Check if A20 Line is enabled.
jnz boot1A20HasAlreadyBeenEnabled # Jump if condition is met.
movw $0x2401, %ax # Enable A20 Line using the BIOS Method.
stc # Set carry flag.
int $0x15 # Call BIOS interrupt 15 (Enable A20 Line).
jc 1f # BIOS Method failed.
testb %ah, %ah # Compares both registers.
jne 1f # Jumps if they're not equal.
call boot1CheckA20LineState # Check if A20 Line is enabled.
jnz boot1A20HasBeenEnabled # Jump if condition is met.
1: # Enable A20 Line using Intel's 8042 Controller Method.
call .boot1_8042_wait # Wait for Intel's 8042 controller to be ready.
movb $0xd1, %al # Prepare the 8042 port write.
outb %al, $0x64 # Write to the 8042 port.
call .boot1_8042_wait # Wait for Intel's 8042 controller to be ready.
movb $0xdf, %al # Prepare the 8042 port write.
outb %al, $0x60 # Enable A20 Line.
call .boot1_8042_wait # Wait for Intel's 8042 controller to be ready.
call boot1CheckA20LineState # Check if A20 Line is enabled.
jnz boot1A20HasBeenEnabled # Jump if condition is met.
# Enable A20 Line using the 'Fast' Method.
inb $0x92, %al # Try the computer's Fast A20 Gate.
testb $0x02, %al # Compare both values.
jnz 1f # Don't enable A20 if it's already set.
orb $0x02, %al # Check wether the A20 Gate Enable Bit...
andb $0xfe, %al # ...is set or not.
outb %al, $0x92 # Enable the A20 Line using the Fast Gate.
1:
call boot1CheckA20LineState # Check if A20 Line is enabled.
jnz boot1A20HasBeenEnabled # Jump if condition is met.
jmp 1b # Check until A20 Line is set.
.boot1_8042_wait: # Function that waits for Intel's 8042 controller to be ready.
inb $0x64, %al # Read 8042's status.
testb $0x02, %al # Test if bit 1 is zero.
jnz .boot1_8042_wait # Jump if condition is met.
ret # Return to parent function.
boot1CheckA20LineState:
pushw %ds
pushw %es
xorw %ax, %ax
movw %ax, %ds
movw $0x200, %si
decw %ax
movw %ax, %es
movw $0x210, %di
movw %ax, %cx
movw (%si), %ax
pushw %ax
1:
incw %ax
movw %ax, %es:(%di)
cmpw %ax, (%si)
loope 1b
popw (%si)
popw %es
popw %ds
ret
boot1LoadGDT:
cli
pusha
lgdt gdt
sti
popa
call boot1LoadedGDTMessage
ret
boot1A20HasBeenEnabled:
mov $.boot1A20HasBeenEnabled, %si
call boot1print
ret
boot1A20HasAlreadyBeenEnabled:
mov $.boot1A20HasAlreadyBeenEnabled, %si
call boot1print
ret
boot1LoadedMessage:
mov $.boot1LoadedMessage, %si
call boot1print
ret
boot1LoadedGDTMessage:
mov $.boot1LoadedGDTMessage, %si
call boot1print
ret
boot1print:
mov $0x0E, %ah
.boot1printchar:
lodsb
cmp $0, %al
je .boot1printdone
int $0x10
jmp .boot1printchar
.boot1printdone:
ret
.code32
start_32:
mov $DATA_SEG, %eax
mov %eax, %ds
mov %eax, %es
mov %eax, %fs
mov %eax, %gs
mov %eax, %ss
mov $PM_STACK, %esp # Set the stack pointer. I place it below the EBDA
.end_loop:
hlt
jmp .end_loop