Hello.

Im trying to write my own kernel. I loaded it with GRUB. Everything was going good until i tried to switch back to real mode. After several hours i managed to do that. Then i tryed to jump back to protected mode. I setuped Gtd, updated cr0 and while far jump emulators just hanged. What i should do to go back to pmode?


format elf executable at 1024*1024
entry _kernel

MB_4KBPAGE_ALIGN = 1 shl 0
MB_MEMORY_INFO  = 1 shl 1

MB_HEADER_MAGIC  = 0x1BADB002
MB_HEADER_FLAGS  = MB_4KBPAGE_ALIGN or MB_MEMORY_INFO
MB_CHECKSUM      = -MB_HEADER_MAGIC-MB_HEADER_FLAGS

SEGMENT_RING0    = 10010000b

SEGMENT_RW      = 0010b
SEGMENT_RE      = 1010b

SEGMENT_4KB      = 1000b
SEGMENT_USE32    = 0100b

macro GDT_ITEM limit,base,access,granularity
{
dw limit and 0xFFFF
dw base and 0xFFFF
db (base shr 16) and 0xFF
db access
db ((limit shr 16) and 0x0F) or ((granularity) shl 4)
db (base shr 24) and 0xFF
}

segment
multiboot_header:
.magic dd MB_HEADER_MAGIC
.flags dd MB_HEADER_FLAGS
.checksum dd MB_CHECKSUM

_kernel:
mov esp, kStack

; Load new GDT and set segment registers.

lgdt

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

; Flush CS.

jmp GDT_RING0_CODE : .pmode_flush

.pmode_flush:

; Load real mode IDT.

lidt

; Copying real mode code to under 1MB.
mov edi, 0x7C000
mov esi, RealModeCode
mov ecx, length.RealModeCode

rep movsb

; Jumping to segment with 64kb limit. Base is set to .rmode_flush.

jmp GDT_RMODE_CODE : 0

.rmode_flush:
org 0
use16
mov ax, GDT_RMODE_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax

; Cleaning PE bit in cr0.

mov eax, cr0
and al, not 1
mov cr0, eax

; Jumping to 0x7C000.
jmp 0x7C00 : 0

org $ + _kernel.rmode_flush
use32

; Code is copied to 0x7c000, so execution will follow from here.

RealModeCode:
org 0x7C00
use16
; Setting segment registers.
mov ax, cs

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

sti

mov ax,0x4F02
mov bx,0x4112
int 10h

cli

; To that part everyting is good.

lgdt

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

; And error occurs here...

jmp RMGDT_RING0_CODE : .back_to_pmode
.back_to_pmode:

hlt

rmGdtr:
.size dw rmGdt_size
.addr dd rmGdt

rmGdt:
GDT_ITEM 0, 0, 0, 0
RMGDT_RING0_CODE = $ - rmGdt

GDT_ITEM 0xFFFFF, 0, (SEGMENT_RING0 + SEGMENT_RE), (SEGMENT_4KB + SEGMENT_USE32)

rmGdt_size = $ - rmGdt - 1

length.RealModeCode = $ - 0x7C00

org $ + RealModeCode - 0x7c00
use32

rmIdtr:
.size dw 1023
.addr dd 0

Gdt:
.size dw Gdt_size
.addr dd Gdt_array
       
Gdt_array:
GDT_ITEM 0, 0, 0, 0
GDT_RING0_CODE = $ - Gdt_array
GDT_ITEM 0xFFFFF, 0, SEGMENT_RING0 or SEGMENT_RE, SEGMENT_4KB or SEGMENT_USE32
GDT_RING0_DATA = $ - Gdt_array
GDT_ITEM 0xFFFFF, 0, SEGMENT_RING0 or SEGMENT_RW, SEGMENT_4KB or SEGMENT_USE32

GDT_RMODE_CODE = $ - Gdt_array
GDT_ITEM 0xFFFF, _kernel.rmode_flush, SEGMENT_RING0 or SEGMENT_RE, 0
GDT_RMODE_DATA = $ - Gdt_array
GDT_ITEM 0xFFFF, _kernel.rmode_flush, SEGMENT_RING0 or SEGMENT_RW, 0
Gdt_size = $ - Gdt_array - 1
       
segment
rb 1024
kStack:
Posted on 2006-08-15 16:11:10 by Trolek
Off-hand, you do not need a "Real Mode IDT", as Interrupts default to the IVT@0000:0000 in Real Mode, so this part of the code is useless.

Part of the problem may be the fact that you did not put "use32" before the ".back_to_pmode" label, which might exclude the needed far jump. At any rate, open-up your loader binary in a hex editor/disassembler and check that area for the far jmp opcode (0xEA).

For more info, read THIS DOC involving Unreal/Voodoo/Flat mode, it incorporates similar concepts as what you are trying to achieve.
Posted on 2006-08-15 23:03:55 by SpooK
I noticed that the main reason that you're going back to real mode is to set the video mode.

If it was just to update the GDT and CR0, you could do that from protected mode, but setting the video mode is one of those excessively painful things to do from protected mode.  If you had infinite time on your hands, an awesome thing to write would be some code that reverse-engineers the real mode code for int 10h,4F02h so that you could dynamically create the driver for it, but sadly, that's probably not going to happen.

Debugging code surrounding mode switches is a pain in the butt; I've definitely been there.  A feasible alternative, though is to not rely on GRUB, and simply write your own bootloader or modify an existing one.  You can check out my Master Boot Record in http://pwnos.googlecode.com/svn/trunk/Boot/Boot.asm  It starts from real mode, disables interrupts, loads the following 62 sectors (31KB), clears gate A20 (though it seems to always be clear already), selects a video mode of the desired resolution and switches to it, loads a GDT and IDT, and switches to protected mode, all in under 512 bytes, with enough room left over for the partition table.  Using a custom bootloader does mean that you'd probably want alternative media to boot from, like a USB key or floppy disk, but it looks like it might actually save you some trouble in this case.  After all, you're writing almost all of your own bootloader anyway, since GRUB doesn't do much for you.

In the near future, I'll be getting the rest of my bootloader working again, and at one point it was reading from NTFS partitions, (which could be swapped out for any other filesystem), and booting dual processors, (it just seems not to work at all on a single processor, which is what my laptop has).

Best of luck to ya!
Posted on 2006-08-15 23:57:48 by hackulous
Instead of switching back and forth between pm/rm, what about setting up a v86 task for BIOS-calling?
Posted on 2006-08-16 02:54:25 by f0dder
Im too lazy to write my own video driver and Im tired of trying to switch from one mode to another. As f0dder said, I should setup a V86 task and give myself a break. Thanks for that. :)
Posted on 2006-08-16 08:13:18 by Trolek