I can't get my protected mode set up to work... Please help!

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
babaliaris
Posts: 13
Joined: Tue Apr 09, 2019 12:34 pm
Libera.chat IRC: babaliaris

I can't get my protected mode set up to work... Please help!

Post by babaliaris »

I read so many tutorials and owned that GDT OsDev wiki page to understand everything, but my code doesn't seem to work...

Bootloader.asm

Code: Select all

[bits 16]

;----------Initialize Segments and Stack----------;
cli

mov ax, 0x7c0
mov ds, ax
mov ss, ax

xor ax, ax
mov es, ax
mov bp, ax
mov sp, ax
mov fs, ax
mov gs, ax

sti
;----------Initialize Segments and Stack----------;


;Load GDT.
call load_gdt

;Enable it.
mov     eax, cr0
or      eax, 0x1
mov     cr0, eax

;Far jump to the 32bit code.
jmp load_gdt_cs:main_32


[bits 32]

main_32:
    ;----------Initialize Segments and Stack----------;


    mov ax, load_gdt_ds
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov eax, 0x90000
    mov ebp, eax
    mov esp, eax
    ;----------Initialize Segments and Stack----------;



    mov ah, 'X'
    mov al, 0x0b
    mov [0xb8000], ax

    jmp $


[bits 16]
;%include "include/print_static.asm"
;%include "include/print_hex.asm"
%include "include/load_gdt.asm"

hello: db "Hello Os!", 0x0

times 510 - ($-$$) db 0x0000
dw 0xAA55

load_gdt.asm:

Code: Select all

load_gdt_start:

    load_gdt_start_null_segment:
        dd  0x0
        dd  0x0

    load_gdt_start_code_segment:
        dw  0xffff
        dw  0x0000
        db  0x00
        db  10011010b
        db  11001111b
        db  0x00

    load_gdt_start_data_segment:
        dw  0xffff
        dw  0x0000
        db  0x00
        db  10010010b
        db  11001111b
        db  0x00

    load_gdt_end:

    load_gdt_start_discriptor:
        dw load_gdt_end - load_gdt_start - 1
        dd load_gdt_start

    load_gdt_cs: equ load_gdt_start_code_segment - load_gdt_start
    load_gdt_ds: equ load_gdt_start_data_segment - load_gdt_start


load_gdt:

    pusha

    cli

    lgdt [load_gdt_start_discriptor]

    sti

    popa
    ret
When I run this using Qemu, I get a blinking screen.

What am I doing wrong?

Thank you.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: I can't get my protected mode set up to work... Please h

Post by bzt »

I've deja vu. I've seen the same insane load_gdt function not long time ago... Yep, here it is (although the OP has changed the original post, it now contains the fixed code).

It looks like you are using the same bad tutorial as he did. Please read through that topic you'll very likely find the solution there, along with many useful advice on how to debug this particular issue.

Cheers,
bzt
babaliaris
Posts: 13
Joined: Tue Apr 09, 2019 12:34 pm
Libera.chat IRC: babaliaris

Re: I can't get my protected mode set up to work... Please h

Post by babaliaris »

It seems that it was because I was using a ds = 0x7C0 . I changed it to ds = 0 and used the [org 0x7c00] and now it works! But the screen is still blinking
when I use the jmp $ to halt the system. But if I disable the interrupts before the jmp $ instruction then it works. Can you tell me why?

This is the updated code:

Code: Select all

[bits 16]
[org 0x7c00]

;----------Initialize Segments and Stack----------;
cli

xor ax, ax
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax

mov ax, 0x7c00
mov bp, ax
mov sp, ax

sti
;----------Initialize Segments and Stack----------;

;Load GDT.
call load_gdt

;Enable it.
mov     eax, cr0
or      eax, 0x1
mov     cr0, eax

;Far jump to the 32bit code.
jmp load_gdt_cs:main_32


[bits 32]

main_32:
    ;----------Initialize Segments and Stack----------;


    mov ax, load_gdt_ds
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov eax, 0x90000
    mov ebp, eax
    mov esp, eax
    ;----------Initialize Segments and Stack----------;



    mov al, 'X'
    mov ah, 0x0c
    mov [0xb8000], ax

    cli
    jmp $


[bits 16]
%include "include/print_static.asm"
%include "include/print_hex.asm"
%include "include/load_gdt.asm"

hello: db "Hello Os!", 0x0

times 510 - ($-$$) db 0x0000
dw 0xAA55
babaliaris
Posts: 13
Joined: Tue Apr 09, 2019 12:34 pm
Libera.chat IRC: babaliaris

Re: I can't get my protected mode set up to work... Please h

Post by babaliaris »

Also, I can't understand why I have to use the offset from the start of the GDT to far jump and why after the far jump I have to set the segment registers to the offset of the data segment from the start of the GDT

load_gdt_cs = the offset of the code segment from the start of the GDT
load_gdt_ds = the offset of the data segment from the start of the GDT

Code: Select all

;Far jump to the 32bit code.
jmp load_gdt_cs:main_32

Code: Select all

mov ax, load_gdt_ds
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
Isn't the GDT supposed to have zero reference (for example 0000:offset) from the beginning of the memory? So why do I need these segments to be non zero?

Also in protected mode, does this formula still applies? segment:offset = segment*16 +offset
I thought in protected mode all memory addresses should be referenced as 0000: offset where the offset is 32bits and its enough to address 4G of memory.
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: I can't get my protected mode set up to work... Please h

Post by nullplan »

babaliaris wrote:But if I disable the interrupts before the jmp $ instruction then it works. Can you tell me why?
You never set up an IDT, so the IDTR is still pointing to the IVT at address 0. That is not a valid protected mode IDT, so if an interrupt arrives in protected mode, the CPU tries to vector it according to newly garbage data. It is likely to cause a GPF, but you don't have a valid handler for those or for a double fault. So the CPU will execute a "stop special cycle" (was that the name?) which on most mainboards is a sign to reboot. Also, you never remapped the PIC, and this code is BIOS boot code, so the BIOS initialized the PIC and PIT to BIOS defaults. Which means you get an interrupt 8 18 times per second.
babaliaris wrote:Also, I can't understand why I have to use the offset from the start of the GDT to far jump and why after the far jump I have to set the segment registers to the offset of the data segment from the start of the GDT
babaliaris wrote:Also, I can't understand why I have to use the offset from the start of the GDT to far jump and why after the far jump I have to set the segment registers to the offset of the data segment from the start of the GDT
After LGDT, the base address of the GDT is written into the GDTR. When you load a segment (and far jumping just loads a segment into CS), then the CPU will read the segment descriptor from that base address plus the segment offset you specified. Bits 0 and 1 will be masked out for this. Permission checks come in with those. And Bit 2 identifies an LDT segment, which wouldn't work for you since no LDT has been loaded.
babaliaris wrote:Also in protected mode, does this formula still applies? segment:offset = segment*16 +offset
No! In protected mode, the base and length of a segment are encoded in the GDT/LDT entry. In your GDT, base address of each segment is 0, and length is 4GB, and that is probably the case for most people. Segment 0 is special in that it is never valid in pmode, so references to it cause an exception. You can address 4GB of memory with just the offset part alone.
Carpe diem!
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: I can't get my protected mode set up to work... Please h

Post by PeterX »

Well, the reason is: Because the CPU manufacturers decided to design it that way.
The "offset" into the GDT is actually a "selector". It selects the "descriptor".
Normally the first entry (after the null descriptor) is the code descriptor. So JMP uses 0x08:x. And the second entry normally is data so you put 0x10 into DS. IF you would use a third descriptor it would belong to the selector 0x18.

And the real-mode formula with the 16 multiplier is obsolete in pmode!

You could make the segments in pmode of any size and with any start address. But normally OS developers use a _flat_ memory model, so the segment starts at (absolute) 0 and goes till the end of memory.

Maybe you juggle around a bit with pmode, and then you will understand.

Greetings
Peter
babaliaris
Posts: 13
Joined: Tue Apr 09, 2019 12:34 pm
Libera.chat IRC: babaliaris

Re: I can't get my protected mode set up to work... Please h

Post by babaliaris »

Thank you guys for the replies! You gave me some really good advice but still, I don't get everything. Well, probably this is because I don't know some things yet. For example:

I don't know that these are yet.
IDT, IDTR, GPF, PIC and PIT
Probably I will learn them in the future.

Also about this:
It looks like you are using the same bad tutorial as he did.
Is true. I was reading this book which I noticed a lot of typos.

Now I found out about http://www.brokenthorn.com/Resources/OSDev6.html which seems to be pretty good. I will start reading it from
scratch, even though I know the basics.
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: I can't get my protected mode set up to work... Please h

Post by PeterX »

babaliaris wrote:For example:

I don't know that these are yet.
IDT, IDTR, GPF, PIC and PIT
Probably I will learn them in the future.
This site's wiki is a good ressource for learning these things.
http://wiki.osdev.org/

IDT, IDTR are similar to GDTR and GDT, but for interrupts and with a slightly different format. PIC is for interrupts, too.
GPF means General Protection Fault.

Happy hacking
Peter
sj95126
Member
Member
Posts: 151
Joined: Tue Aug 11, 2020 12:14 pm

Re: I can't get my protected mode set up to work... Please h

Post by sj95126 »

babaliaris wrote:Also, I can't understand why I have to use the offset from the start of the GDT to far jump
It's actually not an offset, it's an index value. This is an area that causes instant confusion when it comes to segment selectors.

If you do what most people (and tutorials) do, your first two entries in the GDT will be system code and system data. That's indexes 1 and 2 (remember the first entry in the GDT must be all zeros).

Expressed in "segment selector" form (which is what you load into a segment register like CS or DS), index 1 looks like this:

0x8, or in binary: 0000000000001000

Now, remember that the lowest three bits indicate GDT vs. LDT, and the RPL (requested privilege level) of the selector. For system segments in the GDT, these lower bits will usually be 000.

So, your first two segments selectors for the first two descriptors in the GDT will be 0x8 (01 000) and 0x10 (10 000). By coincidence or bad luck, in 32-bit mode with 8-byte segment descriptors, this also HAPPENS to be their byte offsets in the GDT (8 and 16), but that's just a coincidence. That's where it's easy to get confused.

(If you were to follow these two descriptors with user code and data descriptors, those would probably have the segment selectors 0x1b (011 011) and 0x23 (100 011).)
Post Reply