jump from bootloader to kernel

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
Hackerweb
Posts: 2
Joined: Tue Jun 14, 2011 5:55 am

jump from bootloader to kernel

Post by Hackerweb »

Hello everyone! :D can anyone show an example how to jump from bootloader to kernel? i have this code:
Loader.asm:

Code: Select all

[BITS 16]
[ORG 0]

entry:
	cli
	xor cx, cx ;set stack
	mov ds, cx
	mov es, cx
	mov ss, cx
	mov sp, start
	sti
	mov ax, 0x07c0
	mov ds, ax
	mov ax, 0x9000
	mov es, ax
	xor si, si
	xor di, di
	mov cx, 128
	rep movsd
	jmp 0x9000:start ;go to bootloader
start:
	mov ax, cs
	mov ds, ax
	mov ss, ax
	mov si, msg_loading ;show start message
	call printsi
	mov si, msg_enable_gdt
	call printsi
	lgdt [gd_reg] ;load gdt
	mov si, msg_enable_a20
	call printsi
	in al, 0x92 ;enable A20
	or al, 2
	out 0x92, al
	mov si, msg_enter_pm
	call printsi
	mov eax, cr0 ;enter protected mode
	or al, 1
	mov cr0, eax
	jmp 0x8:protectedmode
[BITS 32]
protectedmode:
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov ss, ax
printsi:
	pusha
	.loop:
		lodsb
		test al, al
		jz .quit
		mov ah, 0x0e
		int 0x10
		jmp short .loop
	.quit:
		popa
		ret
gdt:
	dw 0, 0, 0, 0
	db 0xFF
	db 0xFF
	db 0x00
	db 0x00
	db 0x00
	db 10011010b
	db 0xCF
	db 0x00
	dw 0, 0, 0, 0
	db 0xFF
	db 0xFF
	db 0x00
	db 0x00
	db 0x00
	db 10011010b
	db 0xCF
	db 0x00	
gd_reg:
	dw 8192
	dd gdt
msg_loading: db "Operating system is loading...", 0x0A, 0x0D, 0
msg_enable_gdt: db "Loading GDT...", 0x0A, 0x0D, 0
msg_enable_a20: db "Enabling A20 Line...", 0x0A, 0x0D, 0
msg_enter_pm: db "Entering protected mode...", 0x0A, 0x0D, 0

TIMES 510 - ($ - $$) db 0
db 0xAA, 0x55
Kernel.asm:

Code: Select all

[BITS 32]
[GLOBAL _invoke_main]
[EXTERN _kernel_main]
_invoke_main:
	mov esp, 0x200000 - 4
	call _kernel_main
Kernel.c:

Code: Select all

void kernel_main()
{
	for (;;);
}
Loader.asm compiles by "nasm -f bin -o Loader.bin Loader.asm" to Loader.bin and i add it to CD as bootloader... VirtualBox shows all strings which in the end of Loader.asm. Kernel.asm and Kernel.c compiles to Kernel.bin but i cannot understand how to jump from Loader.bin to Kernel.bin... help please [-o<
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: jump from bootloader to kernel

Post by bluemoon »

Jump to the kernel is surprisingly easy, you do it by the jmp instruction! :lol:

Well, before jumping to a kernel, you may want to:
[*] Load the kernel from somewhere.
[*] Perform some checks. (Is the CPU supported? enough memory? etc)
[*] Perform detection that is easier to do under real mode. (like memory map)
[*] Perform some machine state initialization. (like enable A20, setup protected mode, temporary selectors & stack)
[*] Print the "Insert coins to continue" message. :mrgreen:

Obviously either you did not post the full code, or you have not loaded any kernel to jump into...
Hackerweb wrote:can anyone show an example how to jump from bootloader to kernel?
It all depends on your design and requirements, a boot loader can somehow be quite complex. In another word, no, I don't know how to jump to your kernel without knowing your specification.

However, for a generic one, check the wiki Rolling_Your_Own_Bootloader.
User avatar
Chandra
Member
Member
Posts: 487
Joined: Sat Jul 17, 2010 12:45 am

Re: jump from bootloader to kernel

Post by Chandra »

Do you see the jmp instruction in your code. I'm guessing you copied a code snippet from somewhere else and tried to mix your own implementation. Here's a link to suit your needs.
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
Hackerweb
Posts: 2
Joined: Tue Jun 14, 2011 5:55 am

Re: jump from bootloader to kernel

Post by Hackerweb »

that is a problem: i saw lot's of loaders that boot kernel from floppy... but need to boot from CD or hard drive... tried to use that loader and wrote it to CD... it shows errors about reading from floppy

First loader:

Code: Select all

[BITS 16]
[ORG 0]

; сколько цилиндров считывать
%define CYLS_TO_READ 10	

; максимальное количество ошибок чтения
; (повторять операцию чтения необходимо как минимум три раза,
; т.к. ошибки возможны из-за того, что мотор привода не разогнался)
%define MAX_READ_ERRORS 5 
			
; Точка входа:
entry:	
		
	cli ;на время установки стека запретим прерывания
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, start

	sti ;разрешим прерывания
	
	;; Необходимо скопировать загрузчик в 
	;; верхнюю часть доступной памяти, т.к.
	;; текущий код (находящийся по адресу 0x0:0x7c00
        ;; будет переписан загруженными с дискеты данными

	; В DS - адрес исходного сегмента
	mov ax, 0x07c0
	mov ds, ax
 
        ; В ES - адрес целевого сегмента
	mov ax, 0x9000
	mov es, ax

	; Копируем с 0
	xor si, si
	xor di, di
	
	; Копируем 128 двойных слов (512 байт)
	mov cx, 128
	rep movsd

	; Прыжок туда, где теперь находится бутсектор
	; (0x9000: 0)
	jmp 0x9000:start


	; следующий код выполняется по адресу 0x9000:0	
start:

        ; занесем новые значения в сегментные регистры
	mov ax, cs
	mov ds, ax
	mov ss, ax	
	
	; обрадуем пользователя сообщением
	mov si, msg_loading
	call kputs



	; Дальнейшая процедура выполняет чтение цилиндра
	; начиная с указанного в DI плюс нулевой цилиндр (в самом конце)
	; В AX - адрес сегмента в который будут записаны считанные данные

	mov di, 1
	mov ax, 0x290
	xor bx, bx

	
.loop:
	mov cx, 0x50
	mov es, cx

	push di

	; Вычисляем какую головку использовать	
	shr di, 1
	setc dh
	mov cx, di
	xchg cl, ch

	pop di
	
	; Уже все цилиндры считали?
	cmp di, CYLS_TO_READ
	je .quit

	call kread_cylinder

	;; Цилиндр считан по адресу 0x50:0x0 - 0x50:0x2400
	;; (линейный 0x500 - 0x2900)

	;; Выполним копирование этого блока по нужному адресу
	pusha
	push ds	

	mov cx, 0x50
	mov ds, cx
	mov es, ax
	xor di, di
	xor si, si
	mov cx, 0x2400
	rep movsb
	
	pop ds
	popa
	
	; Увеличим DI, AX и повторим все сначала
	inc di
	add ax, 0x240
	jmp short .loop
.quit:	
	
	; Мы считывали начиная с 1-го цилиндра! 
	; (т.к. участок 0x50:0 использовался как буфер данных)
	; теперь он свободен и мы можем считать нулевой цилиндр в него
	
	mov ax, 0x50
	mov es, ax
	mov bx, 0
	mov ch, 0
	mov dh, 0
	call kread_cylinder

	; Прыжок на загруженный код
	jmp 0x0000:0x0700
	
kread_cylinder:
        ;; процедура читает заданный цилиндр
	;; ES:BX - буфер
	;; CH - цилиндр
	;; DH - головка

	; Сбросим счетчик ошибок
	mov [.errors_counter], byte 0
	
	pusha
	
	; Сообщим пользователю, какой цилиндр и головку читаем
	mov si, msg_cylinder
	call kputs
	mov ah, ch
	call kputhex
	mov si, msg_head
	call kputs
	mov ah, dh
	call kputhex
	mov si, msg_crlf
	call kputs

	popa
	pusha
		
.start:	
	mov ah, 0x02
	mov al, 18
	mov cl, 1

	; Прерывание дискового сервиса BIOS
	int 0x13
	jc .read_error

		
	popa
	ret

.errors_counter:	db 0	
.read_error:
        ; Если произошла ошибка, то увеличим счетчик,
	; и выведем сообщение с кодом ошибки
	
	inc byte [.errors_counter]
	mov si, msg_reading_error
	call kputs
	call kputhex
	mov si, msg_crlf
	call kputs
	
	; Счетчик ошибок превысил максимальное значение?
	cmp byte [.errors_counter], MAX_READ_ERRORS
	jl .start
	
	; Ничего не получилось :( 
	mov si, msg_giving_up
	call kputs
	hlt
	jmp short $


	
	
	hex_table:	db "0123456789ABCDEF"
kputhex:
        ; Процедура преобразует число в ASCII-код 
	; его шестнадцатеричного представления и выводит его
	; (Да, я знаю, что это можно сделать четырьмя командами :))
	 
	pusha
	xor bx, bx
	mov bl, ah
	and bl, 11110000b
	shr bl, 4
	mov al, [hex_table+bx]
	call kputchar
	
	mov bl, ah
	and bl, 00001111b
	mov al, [hex_table+bx]
	call kputchar
	
	popa
	ret

	; Процедура выводит символ в AL на экран
kputchar:
	pusha
	mov ah, 0x0E
	int 0x10
	popa
	ret

	; Процедура выводит строку, на которую указывает SI, на экран
kputs:
	pusha
.loop:
	lodsb
	test al, al
	jz .quit
	mov ah, 0x0e
	int 0x10
	jmp short .loop
.quit:
	popa
	ret

	
	; Служебные сообщения
msg_loading:	db "the Operating System is loading...", 0x0A, 0x0D, 0
msg_cylinder:	db "Cylinder:", 0
msg_head:	db ", head:",0
msg_reading_error:	db "Error reading from floppy. Errcode:",0
msg_giving_up:	db "Too many errors, giving up",0x0A,0x0D, "Reboot your system, please", 0
msg_crlf:	db 0x0A, 0x0D,0 	
		
	; Сигнатура бутсектора:	
TIMES 510 - ($-$$) db 0
db 0xAA, 0x55
Secondary loader

Code: Select all

[BITS 16]
[ORG 0x700]

	;; Загрузим в сегментные регистры 0 и установим стек

	cli	
	mov ax, 0
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x700
	sti
	
	;; Выведем приветствие на экран
	mov si, msg_intro
	call kputs

	;; Сообщение о том, что собираемся в PM
	mov si, msg_entering_pmode
	call kputs

	;; Отключим курсор, чтобы не мозолил глаза
	mov ah, 1
	mov ch, 0x20
	int 0x10

	;; Установим базовый вектор контроллера прерываний в 0x20
	mov al,00010001b 
	out 0x20,al 
	mov al,0x20 
	out 0x21,al 
	mov al,00000100b 
	out 0x21,al
	mov al,00000001b	
	out 0x21,al 

	
	;; Отключим прерывания
	cli
	
	;; Загрузка регистра GDTR:	
	lgdt [gd_reg]

	;; Включение A20: 
	in al, 0x92
	or al, 2
	out 0x92, al

	;; Установка бита PE регистра CR0
	mov eax, cr0 
	or al, 1	
	mov cr0, eax  

	;; С помощью длинного прыжка мы загружаем
	;; селектор нужного сегмента в регистр CS
	;; (напрямую это сделать нельзя)
	jmp 0x8: _protected


	;; Эта функция вывода строки работает
	;; в реальном режиме!
	;; (использует прерывание 0x10 BIOS)
kputs:
	pusha
.loop:
	lodsb
	test al, al
	jz .quit
	mov ah, 0x0e
	int 0x10
	jmp short .loop
.quit:
	popa
	ret


	;; Следующий код - 32-битный
[BITS 32]
	;; Сюда будет передано управление
	;; после входа в PM
_protected:	
	;; Загрузим регистры DS и SS селектором
	;; сегмента данных
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov ss, ax

	;; Наше ядро (kernel.bin) слинковано по адресу 2мб
	;; Переместим его туда

	;; kernel_binary - метка, после которой
	;; вставлено ядро
	;; (фактически - его линейный адрес)
	mov esi, kernel_binary

	;; адрес, по которому копируем
	mov edi, 0x200000


	;; Размер ядра в двойных словах
	;; (65536 байт)
	mov ecx, 0x4000

	;; Поехали :)
	rep movsd

	;; Ядро скопировано, передаем управление ему
	jmp 0x200000


gdt:
	dw 0, 0, 0, 0	; Нулевой дескриптор

	db 0xFF		; Сегмент кода с DPL=0 
	db 0xFF		; Базой=0 и Лимитом=4 Гб 
	db 0x00
	db 0x00
	db 0x00
	db 10011010b
	db 0xCF
	db 0x00
	
	db 0xFF		; Сегмент данных с DPL=0
	db 0xFF		; Базой=0 и Лимитом=4Гб	
	db 0x00	
	db 0x00
	db 0x00
	db 10010010b
	db 0xCF
	db 0x00


	;; Значение, которое мы загрузим в GDTR:	
gd_reg:
	dw 8192
	dd gdt

		
msg_intro:	db "Secondary bootloader received control", 0x0A, 0x0D, 0
msg_entering_pmode:	db "Entering protected mode...", 0x0A, 0x0D, 0

	
kernel_binary:	
	incbin 'kernel.bin'
User avatar
Chandra
Member
Member
Posts: 487
Joined: Sat Jul 17, 2010 12:45 am

Re: jump from bootloader to kernel

Post by Chandra »

Hackerweb wrote:that is a problem: i saw lot's of loaders that boot kernel from floppy... but need to boot from CD or hard drive... tried to use that loader and wrote it to CD... it shows errors about reading from floppy

Code: Select all

msg_reading_error:   db "Error reading from floppy. Errcode:",0
Man, are you kidding?

You're off. I'd recommend you to stick with the floppy rather than trying to dirty your hands with booting from CD. Once you are ready enough, you'll realize how silly you were.

And, where on earth is the read sector command?
Last edited by Chandra on Tue Jun 14, 2011 7:20 am, edited 1 time in total.
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: jump from bootloader to kernel

Post by bluemoon »

Hackerweb wrote:that is a problem: i saw lot's of loaders that boot kernel from floppy... but need to boot from CD or hard drive... tried to use that loader and wrote it to CD... it shows errors about reading from floppy
Are you sure you burn the disc with floppy emulation?

Your secondary loader:

Code: Select all

<snip>
kernel_binary:	
	incbin 'kernel.bin'
So you are putting the kernel image right after the 2nd stage code, and supposedly loaded altogether by the first loader.
To jump into the kernel, you just need to translate the address of kernel_binary from real mode seg:off to absolute address which is used in protected mode, (ie address = seg*16+kernel_binary ) and then jmp to it.

If you use a non-zero selector.base you need to adjust it accordingly.
User avatar
Chandra
Member
Member
Posts: 487
Joined: Sat Jul 17, 2010 12:45 am

Re: jump from bootloader to kernel

Post by Chandra »

bluemoon wrote:So you are putting the kernel image right after the 2nd stage code, and supposedly loaded altogether by the first loader.
Now I realize, that's the reason for kernel being not loaded from the 2nd stage loader.
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
Post Reply