I am trying to develop a kernel in C. My kernel is supposed to display a very simple welcome message. The second stage of my bootloader loads the kernel at 0x8000 and it moves the kernel to 0x100000. My kernel consists of two parts. third_stage.asm
which calls the main function in os.c
. The problem is that I keep getting guru meditation error.
The code for my bootloader(boot) can be found in my previous StackOverflow question.
The code for my second stage:
org 0x7E00
bits 16
;;;;;;;;;;;;;stack;;;;;;;;;;
Setup:
cli
xor ax , ax
mov ds , ax
mov es , ax
mov ax , 0x9000
mov ss , ax
mov sp , 0x0000
sti
;;;;;;;;;;;;;video;;;;;;;;;;;
Set:
mov al , 03h
mov ah , 00h
int 10h
mov ah , 09h
mov al , 'A'
mov bh , 00h
mov bl , 0x0F
mov cx , 01h
int 10h
jmp loadgdt
;;;;;;;;;;;;gdt;;;;;;;;;;;;;;;
gdt_start:
null:
dd 0
dd 0
code:
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
data:
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
end:
load: dw end - gdt_start -1
dd null
;;;;;;;;;;;;;loadgdt;;;;;;;;;;
loadgdt:
lgdt [load]
;;;;;;;;;;;;A20;;;;;;;;;;;;;;;
A20:
mov ax , 0x2401
int 0x15
jc A20
;;;;;;;;;;;;;floppy;;;;;;;;;;;
Reset:
mov ah , 00h
mov dl , [0x500]
int 13h
jc Reset
Read:
mov ax , 0x800
mov es , ax ; Setup ES=0x800
mov ah , 02h ; Setup AH
mov al , 0fh ; Setup AL
mov ch , 00h
mov cl , 03h
mov dh , 00h
mov dl , [0x500]
xor bx , bx
int 13h
jc Read
Begin:
mov ah , 09h
mov al , 'G'
mov bh , 00h
mov bl , 0x0F
mov cx , 01h
int 10h
;;;;;;;;;;;switching to protected;;;;
protected:
mov ah , 09h
mov al , 'P'
mov bh , 00h
mov bl , 0x0F
mov cx , 01h
int 10h
xor ax, ax
mov ds, ax
cli
mov eax, cr0
or eax , 1
mov cr0 , eax
jmp (code-gdt_start):transfer_control
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
bits 32
transfer_control:
mov ax, (data-gdt_start)
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h
mov [0xB8000], word 0x0F58 ; Print 'X'
CopyImage:
mov eax , dword 0x0f
mov ebx , dword 0x200
mul ebx
mov ebx , 4
div ebx
cld
mov esi , 0x8000
mov edi , 0x100000
mov ecx , eax
rep movsd
jmp 0x100000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times 512-($-$$) db 0
Code for third_stage:
bits 32
global _start
extern main
_start:
mov ax , 0x10
mov ds , ax
mov ss, ax
mov es, ax
mov esp, 90000h
mov [0xB8002], word 0x0F58 ; Print 'X'
call main
hlt
Code for os.c:
//reserved - 0x500(drive number), 0x501(keyboard buffer), 0x502(screen X) , 0x503(screen y)
volatile char * buffer = (volatile char*)0x501 ;
volatile char * x = (volatile char*)0x502 ;
volatile char * y = (volatile char*)0x503 ;
void newline() {
if(*y < 24) {
*y++ ;
*x = 0 ;
}
else {
*y = 25 ;
*x = 80 ;
}
}
void clrscr() {
volatile char * display = (volatile char*)0xb8000 ;
for(display = 0xb8000 ; display <= 0xbffff;display++) {
*display = 0 ;
}
}
void println(char output[]) {
char * x = (char*)0x502 ;
char * y = (char*)0x503 ;
int arg = 0 ;
if(*x == 80 && *y == 25) {
clrscr() ;
*x = 0 ;
*y = 0 ;
}
volatile char * display = (volatile char*)0xb8000 + (((*y * 80) + *x) * 2) ;
while(output[arg] != '\0') {
if(*x == 80 && *y == 25) {
clrscr() ;
*x = 0 ;
*y = 0 ;
arg = 0 ;
display = 0xb8000 ;
}
else if(*x == 80) {
*y++ ;
*x = 0 ;
}
*display = output[arg] ;
display++ ;
*display = 0x0f ;
display++ ;
arg++ ;
*x++ ;
}
newline() ;
}
void print(char output[]) {
int arg = 0 ;
if(*x == 80 && *y == 25) {
clrscr() ;
*x = 0 ;
*y = 0 ;
}
volatile char * display = (volatile char*)0xb8000 + (((*y * 80) + *x) * 2) ;
while(output[arg] != '\0') {
if(*x == 80 && *y == 25) {
clrscr() ;
*x = 0 ;
*y = 0 ;
arg = 0 ;
display = 0xb8000 ;
}
else if(*x == 80) {
*y++ ;
*x = 0 ;
}
*display = output[arg] ;
display++ ;
*display = 0x0f ;
display++ ;
arg++ ;
*x++ ;
}
}
void printc(char output) {
char * x = (char*)0x502 ;
char * y = (char*)0x503 ;
if(*x == 80 && *y == 25) {
clrscr() ;
*x = 0 ;
*y = 0 ;
}
else if(*x == 80) {
*y++ ;
*x = 0 ;
}
volatile char * display = (volatile char*)0xb8000 + (((*y * 80) + *x) * 2) ;
*display = output ;
display++ ;
*display = 0x0f ;
display++ ;
*x++ ;
if(*x == 80) {
*y++ ;
*x = 0 ;
}
}
void backspace() {
if(*x == 0 && *y == 0) {
}
else if(*x == 0) {
*x = 79 ;
*y-- ;
volatile char * display = (volatile char*)0xb8000 + (((*y * 80) + *x) * 2) ;
*display = 0 ;
display++ ;
*display = 0 ;
}
else {
*x-- ;
volatile char * display = (volatile char*)0xb8000 + (((*y * 80) + *x) * 2) ;
*display = 0 ;
display++ ;
*display = 0 ;
}
}
char * scanln() {
static char input[512] ;
char scancode[0xd9] ;
char shift_scancode[0xd9] ;
*buffer = 0 ;
int arg = 0 ;
scancode[0x1c] = 'a' ;
scancode[0x32] = 'b' ;
scancode[0x21] = 'c' ;
scancode[0x23] = 'd' ;
scancode[0x24] = 'e' ;
scancode[0x2b] = 'f' ;
scancode[0x34] = 'g' ;
scancode[0x33] = 'h' ;
scancode[0x43] = 'i' ;
scancode[0x3b] = 'j' ;
scancode[0x42] = 'k' ;
scancode[0x4b] = 'l' ;
scancode[0x3a] = 'm' ;
scancode[0x31] = 'n' ;
scancode[0x44] = 'o' ;
scancode[0x4d] = 'p' ;
scancode[0x15] = 'q' ;
scancode[0x2d] = 'r' ;
scancode[0x1b] = 's' ;
scancode[0x2c] = 't' ;
scancode[0x3c] = 'u' ;
scancode[0x2a] = 'v' ;
scancode[0x1d] = 'w' ;
scancode[0x22] = 'x' ;
scancode[0x35] = 'y' ;
scancode[0x1a] = 'z' ;
scancode[0x45] = '0' ;
scancode[0x16] = '1' ;
scancode[0x1e] = '2' ;
scancode[0x26] = '3' ;
scancode[0x25] = '4' ;
scancode[0x2e] = '5' ;
scancode[0x36] = '6' ;
scancode[0x3d] = '7' ;
scancode[0x3e] = '8' ;
scancode[0x46] = '9' ;
scancode[0x0e] = '`' ;
scancode[0x4e] = '-' ;
scancode[0x55] = '=' ;
scancode[0x5d] = '\\' ;
scancode[0x54] = '[' ;
scancode[0x5b] = ']' ;
scancode[0x4c] = ';' ;
scancode[0x52] = '\'' ;
scancode[0x41] = ',' ;
scancode[0x49] = '.' ;
scancode[0x4a] = '/' ;
scancode[0x29] = ' ' ;
shift_scancode[0x1c] = 'A' ;
shift_scancode[0x32] = 'B' ;
shift_scancode[0x21] = 'C' ;
shift_scancode[0x23] = 'D' ;
shift_scancode[0x24] = 'E' ;
shift_scancode[0x2b] = 'F' ;
shift_scancode[0x34] = 'G' ;
shift_scancode[0x33] = 'H' ;
shift_scancode[0x43] = 'I' ;
shift_scancode[0x3b] = 'J' ;
shift_scancode[0x42] = 'K' ;
shift_scancode[0x4b] = 'L' ;
shift_scancode[0x3a] = 'M' ;
shift_scancode[0x31] = 'N' ;
shift_scancode[0x44] = 'O' ;
shift_scancode[0x4d] = 'P' ;
shift_scancode[0x15] = 'Q' ;
shift_scancode[0x2d] = 'R' ;
shift_scancode[0x1b] = 'S' ;
shift_scancode[0x2c] = 'T' ;
shift_scancode[0x3c] = 'U' ;
shift_scancode[0x2a] = 'V' ;
shift_scancode[0x1d] = 'W' ;
shift_scancode[0x22] = 'X' ;
shift_scancode[0x35] = 'Y' ;
shift_scancode[0x1a] = 'Z' ;
shift_scancode[0x45] = ')' ;
shift_scancode[0x16] = '!' ;
shift_scancode[0x1e] = '@' ;
shift_scancode[0x26] = '#' ;
shift_scancode[0x25] = '$' ;
shift_scancode[0x2e] = '%' ;
shift_scancode[0x36] = '^' ;
shift_scancode[0x3d] = '&' ;
shift_scancode[0x3e] = '*' ;
shift_scancode[0x46] = '(' ;
shift_scancode[0x0e] = '~' ;
shift_scancode[0x4e] = '_' ;
shift_scancode[0x55] = '+' ;
shift_scancode[0x5d] = '|' ;
shift_scancode[0x54] = '{' ;
shift_scancode[0x5b] = '}' ;
shift_scancode[0x4c] = ':' ;
shift_scancode[0x52] = '"' ;
shift_scancode[0x41] = '<' ;
shift_scancode[0x49] = '>' ;
shift_scancode[0x4a] = '?' ;
shift_scancode[0x29] = ' ' ;
while(*buffer != 0x5a) {
if(*buffer == 0x12) {
*buffer = 0 ;
if(shift_scancode[(int)*buffer] != 0) {
input[arg] = shift_scancode[(int)*buffer] ;
printc(shift_scancode[(int)*buffer]) ;
*buffer = 0 ;
arg++ ;
}
if(*buffer == 0xf0) {
*buffer = 0 ;
*buffer = 0 ;
}
}
else if(*buffer == 0xf0) {
*buffer = 0 ;
*buffer = 0 ;
}
else if(*buffer == 0x66) {
arg-- ;
if(arg >= 0) {
backspace() ;
}
else {
arg = 0 ;
}
}
else if(scancode[(int)*buffer] != 0) {
input[arg] = scancode[(int)*buffer] ;
printc(scancode[(int)*buffer]) ;
*buffer = 0 ;
arg++ ;
}
}
input[arg] = '\0' ;
*buffer = 0 ;
*buffer = 0 ;
return input ;
}
void main() {
char * x = (char*)0x502 ;
char * y = (char*)0x503 ;
*x = 0 ;
*y = 0 ;
println("------------------------------Welcome to Skull OS------------------------------") ;
println("Boot Successful") ;
}
The commands used to make the os are :
nasm third_stage.asm -f elf -o third_stage.o
gcc -m32 -ffreestanding -c os.c -o os.o
ld -m elf_i386 -o os.bin -Ttext 0x100000 os.o third_stage.o --oformat binary
dd seek=0 if=boot of=os.img
dd seek=1 if=second_stage of=os.img
dd seek=2 if=os.bin of=os.img
dd seek=17 if=/dev/zero of=os.img count=1 bs=512
In the beginning I tried to load my OS at 0x8000 but the execution just stops halfway. My OS is being tested under VirtualBox.
One potential problem in stage 2 is this code:
CopyImage:
mov eax , dword 0x0f
mov ebx , dword 0x200
mul ebx
mov ebx , 4
div ebx
DIV EBX divides the 64 bit integer in EDX:EAX by EBX. You don't initialize EDX properly. EDX is the upper 32 bits of the 64-bit number in EDX:EAX so you likely want to set EDX to 0. You could fix it by adding the xor edx, edx
instruction after you calculate the value for EAX. This places zero into EDX. So the code could look like this:
CopyImage:
mov eax , dword 0x0f
mov ebx , dword 0x200
mul ebx
xor edx, edx ; zero extend EAX into EDX
mov ebx , 4
div ebx
Instead of using DIV you can shift EAX to the right by 2 bits and that is the same as division by 4. A command like shr eax, 2
would have worked. But this is all overkill. The assembler can compute this value at assembly time. All of the code above could be reduce to just this instruction:
mov eax, (0x0f * 0x200)/4
That will move the value 0x780 into EAX.
I think one of your bigger issues is how you link your kernel to os.bin
. You do that with:
ld -m elf_i386 -o os.bin -Ttext 0x100000 os.o third_stage.o --oformat binary
Unfortunately when using --oformat binary
the _start
label will not be used to determine the first instruction to appear in the final binary file. As well, the objects will be processed by LD in the order they appear. In your case os.o third_stage.o
would place the code in os.o
before the code in third_stage.o
in the binary file. You need the code in third_stage.o
to appear first because those are the instructions you want loaded at 0x100000. You should do it this way:
ld -m elf_i386 -o os.bin -Ttext 0x100000 third_stage.o os.o --oformat binary
This bug appears to be responsible for your Guru Meditation error with VirtualBox. Without this fix I can reproduce the error, and with the fix the kernel does load and says it was a successful boot.