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?
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:
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.
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.
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!
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!
Instead of switching back and forth between pm/rm, what about setting up a v86 task for BIOS-calling?
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. :)