Unreal Mode Bootloader: Instructions After Far Jump Execute Incorrectly

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
crocodile123
Posts: 1
Joined: Wed Nov 26, 2025 2:42 am
Libera.chat IRC: crocodile

Unreal Mode Bootloader: Instructions After Far Jump Execute Incorrectly

Post by crocodile123 »

Hello,

I am working on a bootloader that enters Unreal Mode to use 32-bit registers while remaining in real mode. I am encountering a problem where far jumps (ljmp) appear to work correctly, but only the first instruction after the jump executes as expected — all subsequent instructions behave incorrectly.

Below is the section of my code that appears to cause the problem:

Code: Select all

# --- Enter Unreal Mode ---
.enter_unreal_mode:

    cli

    pushw   %ds

    lgdt    gdt_ptr

    sgdt    0x1000

    movl    %cr0, %eax
    orl     $1, %eax
    movl    %eax, %cr0

    ljmp    $0x08, $pm_start + 0x7E00

pm_start:

    movw    $0x10, %bx
    movw    %bx, %ds

    movl    %cr0, %eax
    andl    $0xFFFFFFFE, %eax
    movl    %eax, %cr0


    ljmp    $0x0, $unreal+0x7E00

unreal:

    popw    %ds

    sti

    ret
I load the segment descriptors and set up the stack beforehand using the following code:

Code: Select all

# Set all segment registers to the loaded segment
    cli
    movw    $0x07E0, %ax
    movw    %ax, %ds
    movw    %ax, %es
    movw    %ax, %ss
    movw    $0x8000, %sp
    sti
This setup is required because this is stage 2 of the bootloader, which has been loaded by stage 1 at physical address 0x7E00.

After debugging with GDB, I observed that the long jump from real mode to protected mode works correctly, and the long jump from protected mode to Unreal Mode also works initially. However, after executing the first instruction popw %ds, subsequent instructions appear to be corrupted.

Below is the relevant GDB output:

Code: Select all

(gdb) break *0x7E65
Breakpoint 1 at 0x7e65
(gdb) continue
Continuing.

Breakpoint 1, 0x00007e65 in ?? ()
1: x/i $pc
=> 0x7e65:	ljmp   $0xfb1f,$0x7e6a
(gdb) stepi
0x00007e6a in ?? ()
1: x/i $pc
=> 0x7e6a:	pop    %ds
(gdb) stepi
0x00007e6c in ?? ()
1: x/i $pc
=> 0x7e6c:	ret
(gdb) stepi
0x00007e6e in ?? ()
1: x/i $pc
=> 0x7e6e:	add    %cl,%ch
I suspect that this issue may be related to segment or stack setup in Unreal Mode, but I am unable to determine the exact cause. Any guidance or suggestions on resolving this problem would be greatly appreciated.

Thank you for your time and assistance.
Octocontrabass
Member
Member
Posts: 6245
Joined: Mon Mar 25, 2013 7:01 pm

Re: Unreal Mode Bootloader: Instructions After Far Jump Execute Incorrectly

Post by Octocontrabass »

crocodile123 wrote: Wed Nov 26, 2025 2:53 amI am working on a bootloader
Keep in mind, existing bootloaders are a lot less likely to have bugs than a bootloader you write yourself.
crocodile123 wrote: Wed Nov 26, 2025 2:53 amthat enters Unreal Mode to use 32-bit registers
You can use 32-bit registers in plain real mode. Or are you talking about accessing memory above 1MB? If so, you might want to consider using the BIOS instead of unreal mode.
crocodile123 wrote: Wed Nov 26, 2025 2:53 am

Code: Select all

    sgdt    0x1000
What's this for?
crocodile123 wrote: Wed Nov 26, 2025 2:53 am

Code: Select all

    ljmp    $0x08, $pm_start + 0x7E00
    ljmp    $0x0, $unreal+0x7E00
Why isn't your linker calculating the correct offsets for you?
crocodile123 wrote: Wed Nov 26, 2025 2:53 am

Code: Select all

    movw    $0x07E0, %ax
    movw    %ax, %ds
    movw    %ax, %es
    movw    %ax, %ss
    movw    $0x8000, %sp
Why aren't you setting these segment registers to 0? Using nonzero segment registers in real mode makes it more difficult to write and debug code, so you should set segment registers to zero when you can. (Also, this places your stack at a weird address, which might cause problems later if it isn't where you think it should be.)
crocodile123 wrote: Wed Nov 26, 2025 2:53 amI suspect that this issue may be related to segment or stack setup in Unreal Mode, but I am unable to determine the exact cause.
I suspect your segment descriptors are incorrect, but you haven't shared those. I also suspect you're linking your code incorrectly and have misunderstood something about how segmented addressing works.
nullplan
Member
Member
Posts: 2034
Joined: Wed Aug 30, 2017 8:24 am

Re: Unreal Mode Bootloader: Instructions After Far Jump Execute Incorrectly

Post by nullplan »

crocodile123 wrote: Wed Nov 26, 2025 2:53 am I am working on a bootloader that enters Unreal Mode to use 32-bit registers while remaining in real mode.
In addition to everything Octo said, I would counsel against this. Unreal mode is merely a consequence of the way the descriptor caches work. However, no BIOS has official support for it, and any BIOS call or interrupt handler that enters protected mode itself will destroy the unreal mode setup. BIOS has no way of even detecting that unreal mode is in use. Therefore, as long as interrupts are enabled, or after any BIOS routine is called (including the disk drivers you are probably after), the unreal mode setup may be undone at any time.

No, for copying the kernel to the 1MB line, it is far simpler to just use function 87h of interrupt 15h, circumventing the need for your own use of protected mode.
crocodile123 wrote: Wed Nov 26, 2025 2:53 am I load the segment descriptors and set up the stack beforehand using the following code:
You do not. Until you enable protected mode, the segment descriptors are not updated from the GDT when loading their selectors with values.

From the stated purpose, I question the need for the far jump in the first place. Would your goal not be adequately achieved without updating CS? Just load a GDT, enable PM, load DS with a value (and really, you should load ES, too), and disable PM again. Note that this will also set the DS base address, although you can restore that afterward (or just set it correctly in the GDT segment).

The debugger output tells me the next byte after the far jump instruction is 1F, when it should have been C7. So either GDB is confused, something is corrupting your memory, or execution does not continue where you thought it would. Does your CS descriptor have a nonzero base address, perchance?
Carpe diem!
Octocontrabass
Member
Member
Posts: 6245
Joined: Mon Mar 25, 2013 7:01 pm

Re: Unreal Mode Bootloader: Instructions After Far Jump Execute Incorrectly

Post by Octocontrabass »

nullplan wrote: Wed Nov 26, 2025 11:27 amFrom the stated purpose, I question the need for the far jump in the first place.
Anything other than a far jump or far call following a change to CR0.PE is undefined behavior.
nullplan
Member
Member
Posts: 2034
Joined: Wed Aug 30, 2017 8:24 am

Re: Unreal Mode Bootloader: Instructions After Far Jump Execute Incorrectly

Post by nullplan »

Octocontrabass wrote: Wed Nov 26, 2025 1:26 pm Anything other than a far jump or far call following a change to CR0.PE is undefined behavior.
Says who? I looked through the AMD APM vol. 2, and it didn't mention this anywhere the PE bit was discussed. I would have expected such a thing to be mentioned in the chapter on processor initialization, but it wasn't. The Intel SDM says (in volume 3, chapter 11.9.1) that a JMP or CALL is recommended, and indeed that random failures can occur if any other instruction exists there, but it never says it has to be a FAR JMP. So the OP can just as well insert a near JMP to the next instruction there.

But OK, the next chapter does say that a FAR JMP is needed when disabling protected mode, so the OP does need to sort out those linker issues.
Carpe diem!
Post Reply