How can I enter Real Mode after GRUB?

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
Awe2K
Member
Member
Posts: 49
Joined: Sat Oct 24, 2015 3:14 am
Libera.chat IRC: awe2k

How can I enter Real Mode after GRUB?

Post by Awe2K »

Hello, I'm wondering how can I enter RM after booting with GRUB?
I want to go back to RM, then change graphics mode, and then go to PM and execute kernel.
Setting PM bit to zero and STI doesn't do.

Thanks.

Code: Select all

MBOOT_PAGE_ALIGN    equ 1<<0 
MBOOT_MEM_INFO      equ 1<<1
MBOOT_VIDINFO equ  1 << 2       ;;; DOESN'T WORK IN QEMU
MBOOT_HEADER_MAGIC  equ 0x1BADB002 ; Multiboot Magic value
MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)


[BITS 32]

[GLOBAL mboot] 
[EXTERN code]                   ; Start of the '.text' section.
[EXTERN bss]                    ; Start of the .bss section.
[EXTERN end]                    ; End of the last loadable section.

mboot:
    dd  MBOOT_HEADER_MAGIC 
    dd  MBOOT_HEADER_FLAGS 
    dd  MBOOT_CHECKSUM
    
    dd  mboot                   ; Location of this descriptor
    dd  code                    ; Start of kernel '.text' (code) section.
    dd  bss                     ; End of kernel '.data' section.
    dd  end                     ; End of kernel.
    dd  start                   ; Kernel entry point (initial EIP).
;       dd 0 ; # 0 = set graphics mode
;	dd 1024
;	dd 768
;	dd 32
;    ^ ^ ^ Don't work in Qemu

[GLOBAL start]                  ; Kernel entry point.
[EXTERN main]                   ; This is the entry point of our C code

start:

	mov eax, cr0
	and eax,-0x2
	mov cr0,eax ; disable PM

	[bits 16]

       ;; Interrupts already enabled by grub?

	mov ah,0
	mov al,4 ; 320x200x(4 color)
	int 0x10  ; gets crashed

	[bits 32]
   ; push esp - stack ptr
   ; push ebx - mboot ptr



    ; Execute the kernel:
   ; cli                       
   ; call main ; not yet, still in 16 bit real mode
    jmp $


User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: How can I enter Real Mode after GRUB?

Post by Roman »

1) Entering real mode from protected mode is more complicated than you think. You can read about it here.
2) GRUB can setup graphics for you.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
Awe2K
Member
Member
Posts: 49
Joined: Sat Oct 24, 2015 3:14 am
Libera.chat IRC: awe2k

Re: How can I enter Real Mode after GRUB?

Post by Awe2K »

Roman wrote:1) Entering real mode from protected mode is more complicated than you think. You can read about it here.
2) GRUB can setup graphics for you.
1) Yes, I know. I have already read that article.
2) I know, but this doesn't work in qemu (I use qemu/KVM to boot my kernel and debug it)
User avatar
BASICFreak
Member
Member
Posts: 284
Joined: Fri Jan 16, 2009 8:34 pm
Location: Louisiana, USA

Re: How can I enter Real Mode after GRUB?

Post by BASICFreak »

The steps to enter Real Mode (if the data structures are not destroyed) [NOTE this is my way and may not be the best]

1. CLI
2. Setup GDT (with null, 32-bit code, 32-bit data, and 16-bit code)
3. LIDT (the realmode IVT is at 0x0000:0x0000) <- if you skip this step you will not have any Real Mode Ints
4. Switch to 16-bit protected mode (16-bit code segment and 32-bit (or 16) data segment) <- If you skip this step you will triple fault!
5. Switch to 16-bit real mode (set cr0 then jmp Segment:Offset)
6. Setup data segments for real mode
7. STI <- Optional (unless you need maskable ints...)
8. Do things
9. Switch back to PM <- way easier than switching to RM

Just remember all 16-bit code must be under 1MB

If you remapped the PIC then you will have to map it back (assuming reliance on it is needed)

FWIW, this is the reason I wrote my BootStrap and OSLoader - so I could do everything in RM that I wanted to do before the Kernel is executed.

EDIT: Decided to make an untested example:

Code: Select all

MBOOT_PAGE_ALIGN    equ 1<<0 
MBOOT_MEM_INFO      equ 1<<1
MBOOT_VIDINFO equ  1 << 2       ;;; DOESN'T WORK IN QEMU
MBOOT_HEADER_MAGIC  equ 0x1BADB002 ; Multiboot Magic value
MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)


[BITS 32]

[GLOBAL mboot] 
[EXTERN code]                   ; Start of the '.text' section.
[EXTERN bss]                    ; Start of the .bss section.
[EXTERN end]                    ; End of the last loadable section.

mboot:
    dd  MBOOT_HEADER_MAGIC 
    dd  MBOOT_HEADER_FLAGS 
    dd  MBOOT_CHECKSUM
    
    dd  mboot                   ; Location of this descriptor
    dd  code                    ; Start of kernel '.text' (code) section.
    dd  bss                     ; End of kernel '.data' section.
    dd  end                     ; End of kernel.
    dd  start                   ; Kernel entry point (initial EIP).

[GLOBAL start]                  ; Kernel entry point.
[EXTERN main]                   ; This is the entry point of our C code

start:
   cli
   sgdt [OLDGDT]
   sidt [OLDIDT]
   
   lgdt [gdtinfo]
   lidt [idt_real]

   mov eax, cr0
   mov DWORD [OLDCR0], eax
   mov eax, cr3
   mov DWORD [OLDCR3], eax

   jmp 0x08:.protected16bit
[bits 16]
   .protected16bit:
      mov ax, 0x10
      mov ds, ax
      mov es, ax
      mov eax, cr0
      and eax, 0x7FFFFFFE
      mov cr0, eax
      jmp 0:.realmode ; Or Whatever Segment You Need

  .realmode:
    mov ax, 0 ; Or Whatever Segment You Need
    mov ds, ax
    mov es, ax

    ;Setup stack
    mov ss, ax
    mov sp, 0xFFF0

   ; sti ; If you need maskable ints uncomment this

   mov ah,0
   mov al,4 ; 320x200x(4 color)
   int 0x10

   cli
   mov eax, DWORD [OLDCR3]
   mov cr3, eax
   mov eax, DWORD [OLDCR0]
   mov cr0, eax

   ; lgdt [OLDGDT] ; If you changed your GDT reload the old one.

   jmp 18h:.protectedmode
[bits 32]
    .protectedmode:
      mov ax, 0x10
      mov dx, ax
      mov es, ax
      mov ss, ax
      mov sp, [KERNEL_STACK]
      lidt [OLDIDT]
      call main
      .halt:
        hlt
        jmp .halt


;----------------------------------------------------
;                        GDT
;----------------------------------------------------
gdtinfo:
   dw (gdt_end - gdt - 1)   ;last byte in table
   dd (gdt)                ;start of table

gdt         dd 0,0        ; entry 0 is always unused
flatcode    db 0xFF, 0xFF, 0, 0, 0, 10011010b, 10001111b, 0 ; 16-bit code
flatdata    db 0xFF, 0xFF, 0, 0, 0, 10010010b, 11001111b, 0 ; 32-bit data
pmflatcode  db 0xFF, 0xFF, 0, 0, 0, 10011010b, 11001111b, 0 ; 32-bit code
gdt_end:

OLDGDT dd 0
OLDIDT dd 0
OLDCR0 dd 0
OLDCR3 dd 0

idt_real:
  dw 0x3ff    ; 256 entries, 4b each = 1K
  dd 0      ; Real Mode IVT @ 0x0000
Now this will have to be loaded in Real Mode addressable space (below 1MB+64KB)
BOS Source Thanks to GitHub
BOS Expanded Commentary
Both under active development!
Sortie wrote:
  • Don't play the role of an operating systems developer, be one.
  • Be truly afraid of undefined [behavior].
  • Your operating system should be itself, not fight what it is.
Awe2K
Member
Member
Posts: 49
Joined: Sat Oct 24, 2015 3:14 am
Libera.chat IRC: awe2k

Re: How can I enter Real Mode after GRUB?

Post by Awe2K »

BASICFreak wrote:The steps to enter Real Mode (if the data structures are not destroyed) [NOTE this is my way and may not be the best]

1. CLI
2. Setup GDT (with null, 32-bit code, 32-bit data, and 16-bit code)
3. LIDT (the realmode IVT is at 0x0000:0x0000) <- if you skip this step you will not have any Real Mode Ints
4. Switch to 16-bit protected mode (16-bit code segment and 32-bit (or 16) data segment) <- If you skip this step you will triple fault!
5. Switch to 16-bit real mode (set cr0 then jmp Segment:Offset)
6. Setup data segments for real mode
7. STI <- Optional (unless you need maskable ints...)
8. Do things
9. Switch back to PM <- way easier than switching to RM

Just remember all 16-bit code must be under 1MB

If you remapped the PIC then you will have to map it back (assuming reliance on it is needed)

FWIW, this is the reason I wrote my BootStrap and OSLoader - so I could do everything in RM that I wanted to do before the Kernel is executed.

EDIT: Decided to make an untested example:

Code: Select all

MBOOT_PAGE_ALIGN    equ 1<<0 
MBOOT_MEM_INFO      equ 1<<1
MBOOT_VIDINFO equ  1 << 2       ;;; DOESN'T WORK IN QEMU
MBOOT_HEADER_MAGIC  equ 0x1BADB002 ; Multiboot Magic value
MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)


[BITS 32]

[GLOBAL mboot] 
[EXTERN code]                   ; Start of the '.text' section.
[EXTERN bss]                    ; Start of the .bss section.
[EXTERN end]                    ; End of the last loadable section.

mboot:
    dd  MBOOT_HEADER_MAGIC 
    dd  MBOOT_HEADER_FLAGS 
    dd  MBOOT_CHECKSUM
    
    dd  mboot                   ; Location of this descriptor
    dd  code                    ; Start of kernel '.text' (code) section.
    dd  bss                     ; End of kernel '.data' section.
    dd  end                     ; End of kernel.
    dd  start                   ; Kernel entry point (initial EIP).

[GLOBAL start]                  ; Kernel entry point.
[EXTERN main]                   ; This is the entry point of our C code

start:
   cli
   sgdt [OLDGDT]
   sidt [OLDIDT]
   
   lgdt [gdtinfo]
   lidt [idt_real]

   mov eax, cr0
   mov DWORD [OLDCR0], eax
   mov eax, cr3
   mov DWORD [OLDCR3], eax

   jmp 0x08:.protected16bit
[bits 16]
   .protected16bit:
      mov ax, 0x10
      mov ds, ax
      mov es, ax
      mov eax, cr0
      and eax, 0x7FFFFFFE
      mov cr0, eax
      jmp 0:.realmode ; Or Whatever Segment You Need

  .realmode:
    mov ax, 0 ; Or Whatever Segment You Need
    mov ds, ax
    mov es, ax

    ;Setup stack
    mov ss, ax
    mov sp, 0xFFF0

   ; sti ; If you need maskable ints uncomment this

   mov ah,0
   mov al,4 ; 320x200x(4 color)
   int 0x10

   cli
   mov eax, DWORD [OLDCR3]
   mov cr3, eax
   mov eax, DWORD [OLDCR0]
   mov cr0, eax

   ; lgdt [OLDGDT] ; If you changed your GDT reload the old one.

   jmp 18h:.protectedmode
[bits 32]
    .protectedmode:
      mov ax, 0x10
      mov dx, ax
      mov es, ax
      mov ss, ax
      mov sp, [KERNEL_STACK]
      lidt [OLDIDT]
      call main
      .halt:
        hlt
        jmp .halt


;----------------------------------------------------
;                        GDT
;----------------------------------------------------
gdtinfo:
   dw (gdt_end - gdt - 1)   ;last byte in table
   dd (gdt)                ;start of table

gdt         dd 0,0        ; entry 0 is always unused
flatcode    db 0xFF, 0xFF, 0, 0, 0, 10011010b, 10001111b, 0 ; 16-bit code
flatdata    db 0xFF, 0xFF, 0, 0, 0, 10010010b, 11001111b, 0 ; 32-bit data
pmflatcode  db 0xFF, 0xFF, 0, 0, 0, 10011010b, 11001111b, 0 ; 32-bit code
gdt_end:

OLDGDT dd 0
OLDIDT dd 0
OLDCR0 dd 0
OLDCR3 dd 0

idt_real:
  dw 0x3ff    ; 256 entries, 4b each = 1K
  dd 0      ; Real Mode IVT @ 0x0000
Now this will have to be loaded in Real Mode addressable space (below 1MB+64KB)
Thanks, I will try this code to setup proper graphics mode
Awe2K
Member
Member
Posts: 49
Joined: Sat Oct 24, 2015 3:14 am
Libera.chat IRC: awe2k

Re: How can I enter Real Mode after GRUB?

Post by Awe2K »

Also, I've tried to switch to it before using Intel's developer manual.
It said I have to perform same steps as you mentioned. I think now I'm going to read more about GDT's (I suspect my bad one for triple-faulting) :)
Post Reply