Page 1 of 2

How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 1:40 am
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?

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 2:21 am
by Roman
You cannot be sure, that GRUB loads your kernel on a BIOS-based system, it can be EFI, for example.

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 2:35 am
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.

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 2:53 am
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?

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 3:05 am
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)


Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 3:22 am
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.

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 4:26 am
by Kevin
Paging is turned off when control ist passed to a Multiboot kernel, so everything is already identity mapped by definition.

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 4:36 am
by Roman
Kevin wrote:ist
I hope, learning German won't make me speak EnglisCh :D

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 5:39 am
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.

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 5:42 am
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,

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 5:47 am
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

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 5:59 am
by Combuster
Then why don't you write your kernel multiboot compliant and have a custom bootloader for your own use?

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 6:05 am
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.

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 6:25 am
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.

Re: How do I multiboot to real-mode?

Posted: Tue Feb 17, 2015 6:31 am
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.