How do I multiboot to real-mode?

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.
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

How do I multiboot to real-mode?

Post by SoulofDeity »

I'm wanting to roll my own bootloader, but also make it multiboot compatible. To do this, my plan is to create 2 raw binaries; the ipl loaded at 0x7C00 and the spl loaded at 0x7E00. The spl begins with a 16-bit jump followed by the multiboot header and whatnot.

The multiboot stuff is working fine, but I'm having trouble trying to figure out how to return to real mode. Here is what I'm currently doing:

Code: Select all

# Link this file with "--oformat binary -Ttext 0x00007E00".

.set MAGIC,       0x1BADB002
.set AOUT_KLUDGE, 0x00010000

.set FLAGS,       AOUT_KLUDGE
.set CHECKSUM,    -(MAGIC + FLAGS)

.set newidt,      0x0632


.intel_syntax noprefix
.section .text

.code32

.global _start       # _start declared global so the linker doesn't complain
_start:              # This label is here so a 32-bit address is used


.code16

  jmp real16

 .align 4


.code32

multiboot_header:
  .long MAGIC                 # magic
  .long FLAGS                 # flags
  .long CHECKSUM              # checksum
  .long multiboot_header      # header address
  .long _start                # load address
  .long 0x00000000            # load end address
  .long 0x00000000            # bss end address
  .long multiboot_entry       # entry address

multiboot_entry:
  cli                         # disable interrupts
  mov eax, cr0
  and eax, 0x7FFFFFFE         # disable paging and protected mode
  mov cr0, eax
  xor eax, eax
  mov cr3, eax                # reset address of page directory
  ljmp 0x00:real16            # far jump to real mode


.code16

real16:
  cli
  mov sp, 0x7C00              # set up stack
  xor ax, ax                  # initialize segment registers
  mov ds, ax
  mov es, ax
  mov fs, ax
  mov gs, ax
  mov ss, ax
  mov ax, 0x03FF              # limit of the idt
  mov [newidt], ax
  xor ax, ax                  # base of the idt
  mov [newidt + 2], ax
  mov [newidt + 4], ax
  lidt [newidt]               # load new idt
  sti                         # enable interrupts
  mov si, message

print:
  lodsb
  or al, al
  jz hang
  mov ah, 0x0E
  int 0x10
  jmp print

hang:
  jmp hang

message:
  .ascii "Hello World!"

.att_syntax prefix
One thing that is rather suspicious is that if the emulator crashes before entering real mode, the IDTR's base and limit are already 0x0000 and 0x03FF. I seriously hope this doesn't mean that multiboot loaders overwrite the real-mode idt...

The most suspicious thing is that jump. The 'correct' mnemonic for that should be 'jmp dword' or 'jmp far'. GAS doesn't support either of those, but I had read somewhere that ljmp was an older name for it. Anyhow, the CS register doesn't appear to be changing, so I really think the problem is there.


Can anyone tell me what mistake I'm making here?
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: How do I multiboot to real-mode?

Post by Roman »

You cannot be sure, that GRUB loads your kernel on a BIOS-based system, it can be EFI, for example.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: How do I multiboot to real-mode?

Post by Kevin »

Read Intel's documentation for how to do the switch back to real mode. In my edition it's Volume 3a, Chapter 9.9.2 " Switching Back to Real-Address Mode".

For starters, you're supposed to switch to RM-compatible segments before turning PE off.
Developer of tyndur - community OS of Lowlevel (German)
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: How do I multiboot to real-mode?

Post by SoulofDeity »

Kevin wrote:Read Intel's documentation for how to do the switch back to real mode. In my edition it's Volume 3a, Chapter 9.9.2 " Switching Back to Real-Address Mode".

For starters, you're supposed to switch to RM-compatible segments before turning PE off.
So I need to disable paging, jump to a 16-bit protected mode segment, then disable protected mode?
User avatar
Muazzam
Member
Member
Posts: 543
Joined: Mon Jun 16, 2014 5:59 am
Location: Shahpur, Layyah, Pakistan

Re: How do I multiboot to real-mode?

Post by Muazzam »

Code from my OS to switch to real mode from protected mode.

Function:

Code: Select all

;________________________________________________
;Go to 16-bit real Mode
;IN/OUT:  nothing
;
go16:
use32					;Tell assembler to generate 32-bit code
	
	cli			;Clear interrupts
	pop edx			;save return location in edx
	
	jmp 0x20:PM16		;Load CS with selector 0x20

;To go to 16-bit real mode, first we have to go to 16-bit protected mode
;

use16				;Tell assembler to generate 16-bit code
PM16:
	mov ax, 0x28		;0x28 is 16-bit protected mode selector.
	mov ss, ax	
	mov sp, 0x5000		;Stack hase base

	mov eax, cr0
	and eax, 0xfffffffe	;Clear protected enable bit in cr0
	mov cr0, eax		;Disable 32-bit mode

	jmp 0x50:realMode	;Load CS and IP

realMode:
;Load segment registers with 16-bit Values.
;
	mov ax, 0x50
	mov ds, ax
	mov ax, 0x6000
	mov ss, ax
	mov ax, 0
	mov es, ax
	mov sp, 0
	
	cli
	lidt[.idtR]		;Load real mode interrupt vector table
	sti
	
	push 0x50
	push dx			;Return to saved location in EDX
	retf			;Start real mode

;Real mode interrupt vector table
;
.idtR:	dw 0xffff		;Limit
	dd 0			;Base

This is GDT:

Code: Select all

GDT:		dd 0, 0		;null Descriptor
.Code:		dw 0xffff	;Limit (0:15)	
		dw 0x0500	;Base (0:15)
		db 0		;Base (16:23)	
		db 10011010b	;Pr=1,Privl=00,Resr=1,ex=1,C=0,R=1,Ac=0
		db 11001111b	;Gr=1,Sz=1,Resr=00,Limit(16:19)
		db 0		;Base (24:31)


;Data Descriptor base on 500h
.Data:		dw 0xffff	;Limit (0:15)	
		dw 0x0500	;Base (0:15)
		db 0		;Base (16:23)	
		db 10010010b	;Pr=1,Privl=00,Resr=1,ex=0,D=0,W=1,Ac=0
		db 11001111b	;Gr=1,Sz=1,Resr=00,Limit(16:19)
		db 0		;Base (24:31)

;Data Descriptor base on 0h
.linear:	dw 0xffff	;Limit (0:15)	
		dw 0		;Base (0:15)
		db 0		;Base (16:23)	
		db 10010010b	;Pr=1,Privl=00,Resr=1,ex=0,D=0,W=1,Ac=0
		db 11001111b	;Gr=1,Sz=1,Resr=00,Limit(16:19)
		db 0		;Base (24:31)

;Code Descriptor for 16-bit protected mode
.codePM16:	dw 0xffff	;Limit (0:15)	
		dw 0x0500	;Base (0:15)
		db 0		;Base (16:23)	
		db 10011010b	;Pr=1,Privl=00,Resr=1,ex=1,C=0,R=1,Ac=0
		db 0		;Gr=1,Sz=1,Resr=00,Limit(16:19)
		db 0		;Base (24:31)

;Data Descriptor for 16-bit protected mode
.dataPM16:	dw 0xffff	;Limit (0:15)	
		dw 0		;Base (0:15)
		db 0		;Base (16:23)	
		db 10010010b	;Pr=1,Privl=00,Resr=1,ex=0,D=0,W=1,Ac=0
		db 0		;Gr=1,Sz=1,Resr=00,Limit(16:19)
		db 0		;Base (24:31)

GDTReg:		dw 63		;GDT Size - 1
		dd GDT+0x500	;GDT Offset (To be filled latter)

SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: How do I multiboot to real-mode?

Post by SoulofDeity »

@muazzam, thanks man :)

After poking around in the intel guide, I think I can see what I'm doing wrong now. Apparently, I need to load a new GDT with 16-bit descriptors first. I also have to transfer control flow to an "identity mapped linear address" and put the GDT and IDT in identity mapped pages.
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: How do I multiboot to real-mode?

Post by Kevin »

Paging is turned off when control ist passed to a Multiboot kernel, so everything is already identity mapped by definition.
Developer of tyndur - community OS of Lowlevel (German)
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: How do I multiboot to real-mode?

Post by Roman »

Kevin wrote:ist
I hope, learning German won't make me speak EnglisCh :D
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: How do I multiboot to real-mode?

Post by Kevin »

Whoops. It's a typo I often make, but usually I catch it before submitting. :)

Okay, I'll admit it: It ist (!) just another step in the German takeover.
Developer of tyndur - community OS of Lowlevel (German)
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: How do I multiboot to real-mode?

Post by sortie »

This seems crazy. Multiboot is explicitly different from real mode. Why would you even want to do this? Just chain-load if that's what you want to do,
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: How do I multiboot to real-mode?

Post by SoulofDeity »

sortie wrote:This seems crazy.
All I'm doing is making the multiboot loaders load my stage 2 loader instead of my kernel. This way, I can have a choice between a single-boot bootstrap and multiboot loader. It also allows me to more clearly separate my kernel from my bootloader by moving most of the platform specific code into the second stage; this way a majority of the kernel (nearly all of it) can be written in C
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: How do I multiboot to real-mode?

Post by Combuster »

Then why don't you write your kernel multiboot compliant and have a custom bootloader for your own use?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: How do I multiboot to real-mode?

Post by SoulofDeity »

Combuster wrote:Then why don't you write your kernel multiboot compliant and have a custom bootloader for your own use?
Like I said, this is so I can write a light-weight IPL instead of a full-fledged multiboot loader (or using GRUB); and so I can move most of the platform-specific bootstrapping into the stage2 loader to keep my kernel code cleaner.
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: How do I multiboot to real-mode?

Post by Roman »

SoulofDeity wrote:
Combuster wrote:Then why don't you write your kernel multiboot compliant and have a custom bootloader for your own use?
Like I said, this is so I can write a light-weight IPL instead of a full-fledged multiboot loader (or using GRUB); and so I can move most of the platform-specific bootstrapping into the stage2 loader to keep my kernel code cleaner.
You will have platform specific code in your kernel anyway, because platform specific stuff will be required at runtime. Why not have a directory "arch" and subdirectories "x86", "arm"? It would allow you to select targets through just one single environmental variable.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: How do I multiboot to real-mode?

Post by SoulofDeity »

Roman wrote:You will have platform specific code in your kernel anyway, because platform specific stuff will be required at runtime. Why not have a directory "arch" and subdirectories "x86", "arm"? It would allow you to select targets through just one single environmental variable.
I will be doing that. What I'm trying to avoid is having to use linker scripts or writing stubs. Inline assembly I don't mind, I just want to be able to build the kernel as you would a normal program.
Locked