Search code examples
cassemblykernelbootloaderosdev

Cannot load my OS kernel


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.


Solution

  • 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.