Page 1 of 2
jump far after setting up protected mode
Posted: Sun Apr 12, 2020 11:11 am
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.
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 11:22 am
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.
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 11:49 am
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.
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 1:33 pm
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
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 2:40 pm
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?
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 4:02 pm
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).
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 4:07 pm
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?
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 10:21 pm
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.
Re: jump far after setting up protected mode
Posted: Sun Apr 12, 2020 11:15 pm
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.
Re: jump far after setting up protected mode
Posted: Mon Apr 13, 2020 12:16 am
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.
Re: jump far after setting up protected mode
Posted: Mon Apr 13, 2020 2:21 am
by PeterX
Uhmm, just a question:
Shouldn't it be "jmp gdt_code-gdt_start: dword next_instruction"?
Happy hacking
Peter
Re: jump far after setting up protected mode
Posted: Mon Apr 13, 2020 2:34 am
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?
Re: jump far after setting up protected mode
Posted: Mon Apr 13, 2020 2:41 am
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
Re: jump far after setting up protected mode
Posted: Mon Apr 13, 2020 5:24 am
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.
Re: jump far after setting up protected mode
Posted: Mon Apr 13, 2020 7:23 am
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.