user26135221
user26135221

Reputation:

My bootloader isn't printing after switching to Protected Mode

I'm trying to print the character "C" after switching to the Protected Mode, but it isn't working, and I don't know why. I'm using Assembly AT&T/GAS syntax

Here is the code:

.code16
.global _start
_start:

cli

gdt_start:

.quad 0x0

gdt_data:

    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10010010
    .byte 0b11001111
    .byte 0x0

gdt_code:
    .short 0xffff
    .short 0x0
    .byte 0x0
    .byte 0b10011010
    .byte 0b11001111
    .byte 0x0

gdt_end:

DATASEG = gdt_data-gdt_start
CODESEG = gdt_code-gdt_start

gdt_pointer:
    .short gdt_end - gdt_start - 1
    .long gdt_start

lgdt (gdt_pointer)

mov %cr0, %eax
or $1, %eax
mov %eax, %cr0

ljmp $CODESEG, $protected_mode

.code32
protected_mode:

mov $'C', %al
mov 0x0f, %ah
mov %ax, (0xb8000)

jmp .

.fill 510-(.-_start), 1, 0
.word 0xaa55

The Makefile options:

as main.s -o boot.o
ld -Ttext 0x7c00 -o boot.bin --oformat binary boot.o

I think the problem is my GDT table or the far jump, but I don't know where. Anyway, I tried searching for other people's code, but didn't find anything.

Upvotes: 1

Views: 67

Answers (1)

Sep Roland
Sep Roland

Reputation: 39166

I think the problem is my GDT table or the far jump,

  • The GDT does not contain executable code! You must place it out of the execution path; a good place would be right before the .fill directive, so after your infinite loop jmp ..
  • The lgdt (gdt_pointer) and mov %ax, (0xb8000) instructions depend on the DS segment register. You must make sure you set it up correctly. Because you are using an origin of 0x7C00 that would be DS=0 in the first case, and DS=DATASEG with a segment base of 0 in the second case.

For this simplified exercise (printing the character "C" after switching to the Protected Mode), it isn't necessary to setup the stack especially because of the cli, but I have included it anyway:

.code16
.global _start
_start:

cli
xor  %ax, %ax
mov  %ax, %ds
mov  %ax, %ss
mov  $0x7C00, sp

lgdt (gdt_start)

mov  %cr0, %eax
or   $1, %al
mov  %eax, %cr0

ljmp $CODESEG, $PM

.code32
PM:
mov  $DATASEG, %ax
mov  %ax, %ds
mov  %ax, %ss
mov  $0x00007C00, %esp
movw $0x0F43, (0x000B8000)
jmp  .

gdt_start:
    .short gdt_end - gdt_start - 1
    .long  gdt_start
    .short 0x0
gdt_data:
    .short 0xFFFF
    .short 0x0
    .byte  0x0
    .byte  0b10010010
    .byte  0b11001111
    .byte  0x0
gdt_code:
    .short 0xFFFF
    .short 0x0
    .byte  0x0
    .byte  0b10011010
    .byte  0b11001111
    .byte  0x0
gdt_end:

DATASEG = gdt_data - gdt_start
CODESEG = gdt_code - gdt_start

.fill 510-(.-_start), 1, 0
.word 0xAA55

The first slot of the GDT is unused and normally filled with zeroes. However it is possible to store the GDT_POINTER in there and save some of those precious bootsector bytes, considering its restricted size of 512 bytes.

Upvotes: 1

Related Questions