3

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.

Community
  • 1
  • 1
Ajay
  • 318
  • 2
  • 11
  • Shouldn't the linker know where to start since I added the _start label. – Ajay Jan 20 '16 at 04:23
  • No, that doesn't apply when you output with `--oformat binary`, the entry point you specify would have applied if you were outputting a normal _ELF_ executable. Outputting with binary (without a linker script) will force _LD_ to process things in order they appear. Likely the code for `os.o` got placed first in your binary image followed by `third_stage.o`. So the code loaded at 0x100000 was likely not what you were expecting. – Michael Petch Jan 20 '16 at 04:26
  • Sometimes I link the files correctly but the code seems to pause halfway and virtualbox doesn't show any error. What could be causing that? – Ajay Jan 20 '16 at 05:37
  • Did you fix the bug I mentioned in my answer about your div instruction ? And did you switch the objects around as I suggested? I have a bootloader here (first stage) that loads and runs your kernel fine in VirtualBox and qemu – Michael Petch Jan 20 '16 at 05:42
  • I mean like it is supposed to print the welcome message – Ajay Jan 20 '16 at 05:43
  • Yes it print the skull OS banner with a bunch of minus signs – Michael Petch Jan 20 '16 at 05:54
  • Something you may wish to consider is updating your question with your bootloader code. – Michael Petch Jan 20 '16 at 06:16
  • Don't code your own bootloader. Use GRUB and focus on the real work on your operating system. See http://osdev.org/ – Basile Starynkevitch Jan 20 '16 at 06:23
  • Are you booting your kernel from a virtual hard disk image or a virtual floppy in VirtualBox? – Michael Petch Jan 20 '16 at 06:28
  • @MichaelPetch I am booting from a virtual floppy. Does the kernel print the second line "Boot Successful" ? – Ajay Jan 20 '16 at 11:06
  • Yes. It displays boot successful as well as a skull OS message. – Michael Petch Jan 20 '16 at 11:10
  • When I get home (I'm currently in hospital emergency) I'll post my boot loader code (first stage). I used the same commands you duid to build (although I had to guess as your Nasm command for stage 2). I used it as a floppy on my VirtualBox and qemu. Both ran as expected and there was no guru meditation. I did have problems with the ld issue and order of objects but once that was fixed it seems to work. – Michael Petch Jan 20 '16 at 11:14
  • When your fails what is the last thing your boot process prints on the console? – Michael Petch Jan 20 '16 at 11:15
  • Currently, my os prints boot successful and the welcome message on the same line but my original intention was to print it on two different lines. – Ajay Jan 21 '16 at 01:05
  • I changed my pointers to type volatile and that still didnt make an improvement. – Ajay Jan 21 '16 at 06:22
  • I'm no longer getting the guru meditation error. I managed to fix the printing of the strings.I didn't increment the value of pointers properly. Thanks for all the help. – Ajay Jan 21 '16 at 06:33

1 Answers1

4

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.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 1
    You should only use `CDQ` to set up for `IDIV`. You should `xor edx,edx` to set up for `DIV`. Also **nvm the rest of this, I just skimmed** and didn't notice you already made both points >.<. @Ajay should right-shift by 2 instead of using `div` at all. For an unsigned division, the different rounding for negative numbers isn't a factor. Actually unless I'm missing something that rewrites those immediate values in the binary, the OP should **just do the math at assemble time**, like `mov ecx, (0x200 * 0xf) / 4`. – Peter Cordes Jan 20 '16 at 05:56
  • @PeterCordes I typed up cdq quickly on my phone. However I pointed out both the shr solution and the compile time calculation in my original answer. – Michael Petch Jan 20 '16 at 06:10
  • 1
    @PeterCordes The only thing I didn't notice was that he could move it straight to ecx however my answer does make it clear that the compile time solution is probably preferable – Michael Petch Jan 20 '16 at 06:15