jump far after setting up protected mode

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.
slammar
Posts: 24
Joined: Fri Feb 07, 2020 7:35 pm
Libera.chat IRC: slammar

jump far after setting up protected mode

Post by slammar »

NOTE: This post has a small mistake
The code snippet are switched. The one I state that works is the one that doesn't work. My apologies.
What I really wanted to understand is why I have to write the far jump instruction above the 32 bits directive to make it work (and not below, as I mistakenly implied)
Below are the contents of the original post for reference:

Hello, I am using nasm and I am having trouble understanding why the following code doesn't work:

Code: Select all

mov	eax, cr0
or eax, 1
mov	cr0, eax
jmp gdt_code-gdt_start:next_instruction     ; jmp gdt_code-gdt_start:next_instruction --> jmp 0x08:next_instruction

bits 32
next_instruction:
but this work:

Code: Select all

mov	eax, cr0
or eax, 1
mov	cr0, eax

bits 32
jmp gdt_code-gdt_start:next_instruction     ; jmp gdt_code-gdt_start:next_instruction --> jmp 0x08:next_instruction
next_instruction:
The only difference is that I am doing the far jump to set the CS code segment before and after the bits 32 preprocessor directive. I made sure that the code descriptor of the global descriptor table is using 32 bits code, but for some reason the code only works when I put the jump far after the bits 32 directive.

This is how I have set up the global descriptor table:

Code: Select all

gdt_start:
gdt_null: 
    dq 0

gdt_code:
    dw 0xFFFF       ; Segment Limit 15:00
    dw 0x0000       ; Base Address 15:00
    db 0x00         ; Base Address 23:16
       ; Present    Privilege       Descriptor type     Executable      Conforming      Readable    Accessed
    db 0b1__________00______________1___________________1_______________0_______________1___________0
       ; Granularity    32 bits     64 bits     Available   Segment Limit 19:16
    db 0b1______________1___________0___________0___________1111
    db 0x00         ; Base 31:24

gdt_data:
    dw 0xFFFF       ; Segment Limit 15:00
    dw 0x0000       ; Base Address 15:00
    db 0x00         ; Base Address 23:16
       ; Present     Privilege      Descriptor type     Executable      Expansion-direction     Write-enable    Accessed
    db 0b1___________00_____________1___________________0_______________0_______________________1_______________0
       ; Granularity    32 bits     64 bits     Available   Segment Limit 19:16
    db 0b1______________1___________0___________0___________1111
    db 0x00         ; Base 31:24

gdt_end:

gdtr_start: 
    dw gdt_end - gdt_start - 1  ; limit (Size of GDT - 1)
    dd gdt_start
In particular, what I mean when I say that "doesn't work" is that VirtualBox throws a Guru Meditation error, and in Qemu the screen blinks like if it were resetting constantly.

Thanks for your support.
Last edited by slammar on Mon Apr 13, 2020 7:28 am, edited 1 time in total.
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: jump far after setting up protected mode

Post by Schol-R-LEA »

I'm a bit tired at the moment, but on the surface I would say it is because you are trying get the assembler to generate a 32-bit jump when it is still emitting 16-bit code. However, I am not certain that this wouldn't just result in an assembler error. I'll try to get back to you on this when I am thinking clearly, though I expect that someone else will give a better answer soon anyway.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: jump far after setting up protected mode

Post by iansjack »

Surely the far jump is perfectly valid in 16-bit mode - it's just that it points to a completely different address. The pointer will have been assembled as a 16-bit pointer, which is wrong.

Inspect the generated code and see how it differs.
slammar
Posts: 24
Joined: Fri Feb 07, 2020 7:35 pm
Libera.chat IRC: slammar

Re: jump far after setting up protected mode

Post by slammar »

This is the listing when the code works well:

Code: Select all

    14 00000019 0F20C0                  mov	eax, cr0
    15 0000001C 6683C801                or eax, 1
    16 00000020 0F22C0                  mov	cr0, eax
    17 00000023 EA[2800]0800            jmp gdt_code-gdt_start:next_instruction
    18                                  
    19                                  bits 32
    20                                  next_instruction:
And this is the listing when the code doesn't work:

Code: Select all

    14 00000019 0F20C0                  mov	eax, cr0
    15 0000001C 6683C801                or eax, 1
    16 00000020 0F22C0                  mov	cr0, eax
    17                                  bits 32
    18                                  
    19 00000023 EA[2A000000]0800        jmp gdt_code-gdt_start:next_instruction
    20                                  next_instruction:
Attached is the complete source code of my kernel, which I simplified as much as possible, just in case you want to take a look at it. (just 71 lines of code)
The kernel is compiled and tested with the following commands:

Code: Select all

nasm -l kernel.lst kernel.asm ; \
qemu-system-x86_64 -drive format=raw,file=kernel
Attachments
kernel.asm
(2.05 KiB) Downloaded 48 times
slammar
Posts: 24
Joined: Fri Feb 07, 2020 7:35 pm
Libera.chat IRC: slammar

Re: jump far after setting up protected mode

Post by slammar »

In this tutorial the author does the far jump before the 'bits 32' directive: http://www.brokenthorn.com/Resources/OSDev8.html (See the last code snippet at the botton of the webiste, right before and after it says "ENTRY POINT FOR STAGE 3")

Also in this website there are some other examples that show how to set up protected mode, also using the far jump before 'bits 32': http://www.osdever.net/tutorials/view/t ... ected-mode
There are 2 code snippets: this one

Code: Select all

mov eax, cr0
or eax, 1
mov cr0, eax
And this one right after the previous one:

Code: Select all

   jmp 08h:clear_pipe

[BITS 32]
clear_pipe:
Apparently, the correct way to set up the CS segment register is to use the 16 bits version of a far jump after entering protected mode. But, why? If you just got into 32 bits protected mode, why do you have to set up the code segment register using the 16 bits version of far jump instead of the 32 bits version?
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: jump far after setting up protected mode

Post by kzinti »

The 32 bits jump works and the 16 bits jump doesn't work. So why do you insist that the 16 bits jump is the correct one to use?

Look at the code generate for the 16 bits jump: the offset is "0x2800". The offset for the 32 bits jump is "0x2A000000". Clearly the two are not trying to jump to the same address.

What can we conclude? That tutorial has a bug (in fact it is known to have many problems).
User avatar
Minoto
Member
Member
Posts: 89
Joined: Thu May 12, 2011 7:24 pm

Re: jump far after setting up protected mode

Post by Minoto »

slammar wrote: Apparently, the correct way to set up the CS segment register is to use the 16 bits version of a far jump after entering protected mode. But, why? If you just got into 32 bits protected mode, why do you have to set up the code segment register using the 16 bits version of far jump instead of the 32 bits version?
If the purpose of the jump is to reload CS with the new 32-bit segment descriptor you've set up in the GDT, what type of segment is your code executing in at the time of the jump?
Last edited by Minoto on Sun Apr 12, 2020 11:29 pm, edited 1 time in total.
Those who understand Unix are doomed to copy it, poorly.
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: jump far after setting up protected mode

Post by Gigasoft »

Notice how the "working" and "not working" versions switched places between the OPs first and second post. So, there was no problem after all.
User avatar
Minoto
Member
Member
Posts: 89
Joined: Thu May 12, 2011 7:24 pm

Re: jump far after setting up protected mode

Post by Minoto »

Gigasoft wrote:Notice how the "working" and "not working" versions switched places between the OPs first and second post. So, there was no problem after all.
Yes, mis-stating the problem didn't help anyone. I'd like to think it was a simple, honest mistake, though.
Those who understand Unix are doomed to copy it, poorly.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: jump far after setting up protected mode

Post by iansjack »

There certainly is a problem.

In the "wrong" example the jmp is encoded in 16-bit mode and so generates a 16-bit offset. But when running this code the processor is expecting a 32-bit offset, and interprets the code in this way. (It doesn't know, or care, how the code was compiled.) Not only is the offset wrong, but it also reads the next two bytes of code as part of the jmp.

Remeber that the 16BIT and 32BIT directives are not instructions to the processor. They are giving information to the assembler, saying "We're in xx-bit mode so generate the appropriate code. Trust me, I know what I'm doing." Turns out that, in the wrong example, that trust was misplaced. I guess an intelligent enough assembler could analyze the code, determine which mode the processor is in, and generate the appropriate code without any directives. Nasm is not that assembler.

Note that this problem doesn't arise when transitioning from 32- to 64-bit mode as the jmp here has to be in compatibility mode, with a 32-bit offset.
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: jump far after setting up protected mode

Post by PeterX »

Uhmm, just a question:

Shouldn't it be "jmp gdt_code-gdt_start: dword next_instruction"?

Happy hacking
Peter
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: jump far after setting up protected mode

Post by iansjack »

Surely that would be invalid in 16-bit mode, where pointers are words, and unnecessary in 32-bit mode, where pointers are dwords?
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: jump far after setting up protected mode

Post by PeterX »

iansjack wrote:Surely that would be invalid in 16-bit mode, where pointers are words, and unnecessary in 32-bit mode, where pointers are dwords?
No, this is definitively NOT wrong, at least when jumpin to pmode. There is a special opcode byte which is positioned as a prefix. And then CS is loaded with a 32 byte value. That's the correct way to go to pmode.
There's a kind of "state-in-between" when CR0 is enabled but CS is still 16bit!

Greetings
Peter
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: jump far after setting up protected mode

Post by iansjack »

I think the problem is with the IP (offset) part of the address, not the CS.

In any case, the OP states that it works fine when the 32BIT directive is in the correct place.
slammar
Posts: 24
Joined: Fri Feb 07, 2020 7:35 pm
Libera.chat IRC: slammar

Re: jump far after setting up protected mode

Post by slammar »

Gigasoft wrote:Notice how the "working" and "not working" versions switched places between the OPs first and second post. So, there was no problem after all.
My apologies. In fact, the code snippets that are switched are in the first post. My second post, (the one that shows the listings) is the one that points the code that works and doesn't work
iansjack wrote:I think the problem is with the IP (offset) part of the address, not the CS.

In any case, the OP states that it works fine when the 32BIT directive is in the correct place.
Actually, the opposite. If the jump instructions comes before the 32 bits directive it works even though at this point the jump instruction itself should be executing in p-mode. When the jump instruction comes after the bits 32 directive it doesn't work and start blinking in qemu, and throws a Guru Meditation in VBox.

Seriously, sorry for the inconvenience. In case of doubt you can download the source code I shared in previous post in this thread and compile and test it with the command I wrote there. Also you might want to take a look at the tutorial I said I was following, which also uses the jump far instruction before the bits 32 directive but after setting up the p-mode by setting the first bit of cr0.
Post Reply