3

I am writing a boot loader using FASM (the Flat Assembler). I was successful in 16-bit mode but I am facing an error in switching to 32-bit mode. I looked at an answer to a similar (infact same problem at GPF after far jump to protected mode) but the solution doesn't solve my problem.

Here's my boot loader -

org 0x7c00

jmp main

include 'bios.asm'
include 'print32.asm'
include 'gdt.asm'

main:

mov bp,0x9000
mov sp,bp

mov bx, bootMsg
call print_string

lgdt [gdt_descriptor]
cli
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm   ;**The error seems to occurs here

jmp $

bits = 32

init_pm:
    mov ax,DATA_SEG
    mov ds,ax
    mov ss,ax
    mov es,ax

    mov ebp, 0x90000
    mov esp, ebp
    jmp BEGIN_PM

BEGIN_PM:
    mov ebx, pmMsg
    call print_string32
    jmp $

pmMsg:
    db "Sucessfully switched to the 32-bit protected mode....",0

bootMsg:
    db "Booted in 16-bit Real Mode mode....",0

times 510-($-$$) db 0
dw 0xaa55

Here's the GDT -

gdt_start:
gdt_null:
    dd 0x0
    dd 0x0

gdt_code:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0

gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start 

Hers is th Bochs console output -

00478171069i[BIOS  ] Booting from 0000:7c00
00478195765e[CPU0  ] write_virtual_checks(): write beyond limit, r/w
00478195765e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00478195765e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00478195765i[CPU0  ] CPU is in protected mode (active)
00478195765i[CPU0  ] CS.mode = 32 bit
00478195765i[CPU0  ] SS.mode = 32 bit
00478195765i[CPU0  ] EFER   = 0x00000000
00478195765i[CPU0  ] | EAX=d88e0010  EBX=00007d77  ECX=00090000  EDX=00000000
00478195765i[CPU0  ] | ESP=00009000  EBP=00000000  ESI=000e0000  EDI=0000ffac
00478195765i[CPU0  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00478195765i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00478195765i[CPU0  ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00478195765i[CPU0  ] |  DS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00478195765i[CPU0  ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00478195765i[CPU0  ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00478195765i[CPU0  ] |  FS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00478195765i[CPU0  ] |  GS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00478195765i[CPU0  ] | EIP=00007d2f (00007d2f)
00478195765i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00478195765i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
00478195765i[CPU0  ] 0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
00478195765e[CPU0  ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting

Can anyone please help me with this? This has been troubling me since long..

EDIT-

Here's the print32 code-

use32

VIDEO_MEM equ 0xb8000
W_O_B equ 0x0f

print_string32:
    pusha
    mov edx,VIDEO_MEM

print_string32_loop:
    mov al, [ebx]
    mov ah, W_O_B
    cmp al,0
    je print_string32_end
    mov [edx],ax
    inc ebx
    add edx,2
    jmp print_string32_loop

print_string32_end:
    popa
    ret

And the changed code for the bootloader -

org 0x7c00

mov bp,0x9000
mov sp,bp

mov bx, bootMsg
call print_string

cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp 0x8:init_pm

jmp $

use32

init_pm:
    mov ax, 0x10
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov ebp,0x90000
    mov esp,0x90000

    jmp BEGIN_PM

jmp $

include 'bios.asm'
include 'gdt.asm'
include 'print32.asm'

use32

BEGIN_PM:
    mov ebx, pmMsg
    call print_string32
    jmp $

pmMsg:
    db "Sucessfully switched to the 32-bit protected mode....",0

bootMsg:
    db "Booted in 16-bit Real Mode mode....",0

times 510-($-$$) db 0
dw 0xaa55
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Anish Sharma
  • 314
  • 3
  • 18
  • Did you try to use Bochs internal debugger to step through the instructions to find out which one faults? I might be reading it wrong but it seems that you made it into protected mode and then it failed at an instruction like `dword ptr ds:[eax], eax` at address 0x7d2f. _EAX_ seems to contain junk and is being used as a pointer, and DS seems to be set to a segment selector of 0 where I would have expected it to be 0x10. – Michael Petch Sep 19 '16 at 15:56
  • Not related to your problem but `mov bp,0x9000` `mov sp,bp` is not an appropriate way to set a real mode stack pointer. You should set _SS_ followed by SP. If you want the stack at 0x0000:0x9000 or physical address 0x9000 then do `xor ax, ax` `mov ss, ax` `mov sp, 0x9000` – Michael Petch Sep 19 '16 at 16:01
  • I didn't use any debugger but when jmp CODE_SEG:init_pm isn't there, the system stops without any error but bochs doesn't inform about the switch to protected mode. – Anish Sharma Sep 19 '16 at 16:10
  • To be quite frank `or dword ptr ds:[eax], eax` seems like a nonesense instruction. Almost suggesting you might have been executing data as instructions – Michael Petch Sep 19 '16 at 16:10
  • Using a debugger will be the best thing you can do because you would be able to determine if `jmp CODE_SEG:init_pm` actually succeeded. Without seeing all the code for bios.asm, print32.asm, gdt.asm and your compete bootloader it is hard to say. – Michael Petch Sep 19 '16 at 16:14
  • Actually I just noticed something in your code that might be causing issues . You have `bits = 32` . You should have `bits 32` without the equal sign. Likely your instructions are still being encoded as 16-bit when they should be 32-bit, and when run in protected mode they are being decoded incorrectly. `bits = 32` would have just been setting a constant value called `bits` with the value 32 which is not what you want. Try `bits 32` and let me know what happens. – Michael Petch Sep 19 '16 at 16:16
  • The indications are though that you are in protected mode because it does say `CPU is in protected mode (active)` and it says `CS:0008` meaning that the JMP instruction had to have at least successfully executed to properly set CS to CODE_SEG (0x0008). If the jump had failed _CS_ would likely not be set properly. – Michael Petch Sep 19 '16 at 16:31
  • I am using flat assembler and when I used bits 32 the compiler gets an error. So I used bits = 32 which compiles well. – Anish Sharma Sep 19 '16 at 16:31
  • The problem is likely in code you haven't shown. You need to post a complete example which can be used to verify the problem. – Ross Ridge Sep 19 '16 at 16:32
  • Fine I might not be able to post it now, as I am not using a PC. So I will post the code as soon as possible and let you know. Thanks – Anish Sharma Sep 19 '16 at 16:34
  • 1
    No @RossRidge: I am absolutely positive now that the problem is in the code they are showing, and it is because they aren't setting 32-bit instruction generation properly. And I think what happened was the code after `init_pm` got decoded wrong and the strange `dword ptr ds:[eax], eax` instruction was decoded from parts of these instructions `mov ebp, 0x90000` `mov esp, ebp` – Michael Petch Sep 19 '16 at 16:35
  • I have never used _FASM_ the code looked like _NASM_ (your question didn't say). `bits =32` likely didn't set instruction generation to 32-bit as you think it did and is the cause of the issues. If this is _FASM_ you'll need to find the proper way to change to 32-bit instructions – Michael Petch Sep 19 '16 at 16:37
  • Should be easy enough to confirm that Michael's idea is correct, using a disassembler (or the BOCHS built-in debugger / disassembler, which you should definitely be using to reduce the amount of trial and error) – Peter Cordes Sep 19 '16 at 17:01

1 Answers1

3

TL;DR : To fix change bits = 32 to use32

To get FASM to generate instructions for a processor running in 32-bit mode. The FASM Documentation states in Section 1.1.4 Output Formats :

By default, when there is no format directive in source file, flat assembler simply puts generated instruction codes into output, creating this way flat binary file. By default it generates 16-bit code, but you can always turn it into the 16-bit or 32-bit mode by using use16 or use32 directive.

It appears you used bits = 32 when converting NASM code to FASM and bits 32 wasn't accepted by FASM. bits=32 sets a constant value named bits to the value 32. It doesn't tell FASM to generate instructions to be used by a processor in 32-bit mode. Although bits = 32 assembled without error, it did not do what you expected.

By not using use32 you told FASM to generate code after init_pm with instructions that use 32-bit addresses and operands that work in 16-bit real mode, rather than instructions that use 32-bit addresses and operands that work in 32-bit protected mode.


Although I can't test your code I'm going to make these observations while I was trying to understand what might have been going on with the code you did post. First of all BOCHS dumps these lines:

[CPU0  ] 0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
[CPU0  ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting

This says that at address 0x7d2f the instruction or dword ptr ds:[eax], eax was encountered (its encoding being 0900) and generated exception 13 (General Protection Fault).

There are things in the BOCHS state dump at the time that suggest you are in protected mode:

CPU is in protected mode (active)

Additionally there is an indication that jmp CODE_SEG:init_pm executed properly. This is suggested by the fact that at the time of your error BOCHS dumped CS:0008 which means that CS was set to the value 0008 (= CODE_SEG). The DS selector is 0 which is unusual given that after the JMP you set it to DATA_SEG (0x10) BUT the ES and SS segment selectors were set to 0x10. This all suggests the init_pm code was executed but somehow it ended up not executing as expected.

At this point I realized you had written bits = 32 which effectively sets a constant to the value 32. It doesn't tell FASM to generate code that will be targeting a CPU that will be executing in 32-bit mode.

With this in mind I decided to take the instructions and have the assembler encode them for 16-bit mode:

init_pm:
    mov ax,DATA_SEG
    mov ds,ax
    mov ss,ax
    mov es,ax

    mov ebp, 0x90000

When I dumped my test code with NDISASM using the -b32 option (to force NDISASM to decode as a 32-bit target), it decoded it to:

00000000  B810008ED8        mov eax,0xd88e0010
00000005  8ED0              mov ss,eax
00000007  8EC0              mov es,eax
00000009  66BD0000          mov bp,0x0
0000000D  0900              or [eax],eax
0000000F  6689EC            mov sp,bp

The incorrect decoding first did mov eax, 0xd88e0010. This explains why in your BOCHS dump you have EAX=d88e0010 . The lower 16-bits of EAX got moved to ES so ES=0x0010 which matches your BOCHS output ES:0010 . Similar thing applies to SS being set. BP was set to 0 which is confirmed in the BOCHS output BP:0000. This instruction causes the fault and crash:

0000000D  0900              or [eax],eax

or [eax],eax is the same as or ds:[eax],eax . [eax] has an implicit reference to DS . Compare this instruction to the BOCHS output:

0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900

AHA, that is where this unusual instruction came from (you can ignore the DWORD PTR). The incorrectly decoded instruction attempts to use DS which point to the NULL (0x0000) descriptor. This causes a processor fault, and the subsequent error reported by BOCHS and the state dump.


As I stated in the comments, using the internal debugger in BOCHS is valuable especially when debugging bootloaders and kernels. If you had stepped through the bootloader instructions one at a time in the debugger you would have probably found that your FAR JMP to init_pm worked as expected. You would have then observed unexpected instructions being executed that eventually led to a processor fault.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Sorry for the late reply, but thanks !! It works. No error is displayed now. But another problem arose - Bochs now doesn't output any switch to protected mode and the print32 isn't printing anything. Can you help me find out what's the problem? I have edited the code and posted the print32.asm file. – Anish Sharma Sep 21 '16 at 05:22
  • @AneeshSharma . Your new problem should be a new question, and probably solved by using a debugger, however as to why your print doesn't work I don't see where you actually update video memory. Seems like you might be missing `mov [edx], ax` AFTER `je print_string32_end`. – Michael Petch Sep 21 '16 at 05:30
  • That was a silly mistake by me. I added that but it still doesn't work. – Anish Sharma Sep 21 '16 at 05:40
  • Yes it does print that – Anish Sharma Sep 21 '16 at 05:47
  • @AneeshSharma Now that you have a `use32` did you make sure that your 16-bit code in `bios.asm` has a `use16`? – Michael Petch Sep 21 '16 at 05:50
  • Fine I'll do that – Anish Sharma Sep 21 '16 at 05:52
  • I have added the question. Can you check it please - http://stackoverflow.com/questions/39608643/printing-text-in-protected-mode-doesnt-work – Anish Sharma Sep 21 '16 at 06:07