Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disk Read Error while loading sectors into memory

I tried to develop a bootloader using this, but when it is run it shows:

disk read error!

If I ignore it, in a later part, it shows me wrong memory mapping. I also followed some other sources too but in vain. It feels like I'm just copying what they are doing. If I do even a little different a new kind of error generates every time.

Should I use an already built bootloader or what to do?

The code of disk load error is as follow:

[org 0x7c00]

    KERNEL_OFFSET equ 0x1000    
    mov [BOOT_DRIVE], dl        
    mov bp, 0x9000          
    mov sp, bp  
    mov bx, MSG_REAL_MODE       
    call print_string           
    call load_kernel            
    jmp $

print_string:
    pusha
    mov ah, 0x0e

loop:
    mov al,[bx]
    cmp al, 0
    je return
    int 0x10
    inc bx
    jmp loop

return:
    popa
    ret

disk_load: 
    push dx                                              
    mov ah, 0x02                                   
    mov al, dh                                          
    mov ch, 0x00                                    
    mov dh, 0x00                                     
    mov cl, 0x02                                    
    int 0x13                                           
    jc disk_error                                  
    pop dx                                               
    cmp dh, al                                         
    jne disk_error                                 
    ret

 disk_error :
   mov bx, DISK_ERROR_MSG 
   call print_string 
   jmp $

DISK_ERROR_MSG db "Disk read error!", 0

[bits 16]

load_kernel: 
    mov bx, KERNEL_OFFSET       
    mov dh, 15           
    mov dl, [BOOT_DRIVE]                      
    call disk_load                                                  
    ret

; Global variables
BOOT_DRIVE     db 0 
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 

; Bootsector padding 
times 510-($-$$) db 0 
dw 0xaa55

I use this command to assemble and run my bootloader:

nasm boot.asm -f bin -o boot.bin && qemu-system-i386 boot.bin

I get stuck at this point. My bootloader display disk read error. If I ignore it at this point in time, then it creates problems while executing my kernel.c It seems to use wrong memory mapping.

like image 491
Palvit Garg Avatar asked Dec 11 '15 05:12

Palvit Garg


1 Answers

"He's making a list, he's checking it twice..."

  • Your bootloader starts in the real address mode, so it is best to force your assembler in using 16-bit code. You achieve this in NASM by writing [bits 16] at the top of your program.

  • When your bootloader starts, BIOS will have placed it at linear address 00007C00h. It can do this in a number of ways with respect to the combination of segment and offset.
    When you explicitly wrote [org 0x7C00] you (kind of) expected this combination to have the segment part equal to zero. But this is by no means an obligation for BIOS! And so it is up to you to set the segment registers (DS, ES, and SS) manually.

  • The BIOS teletype function that you use in your print_string routine uses BL and BH as parameters. So you should never use the BX register to address your text. Sure, some BIOSes don't use these BL and BH parameters (any more) but do try to develop programs for the biggest audience.

  • When you initialized the SP register with 0x9000 you effectively set up a stack that could easily, without you noticing it, overwrite the program beneath it! It would be best to choose a combination of SS and SP that satisfies your needs and nothing more. A 4608 bytes stack that stays above the bootsector at 7C00h and ends at 9000h would require: SS=07E0h SP=1200h. To avoid any problems on 8086 hardware it's best to disable interrupts when changing SS:SP.

  • You used pusha and popa instructions. These are not valid instructions on 8086 hardware. When writing robust software, we should test if the hardware is up to the task. But here the simplest solution is to only push/pop single registers.

  • You have interpreted the return value from the BIOS function that reads from the disk, but you just abort when an incorrect number of sectors were transferred. This is a wrong approach. When BIOS tells you about an incomplete transfer (this can happen if your BIOS is not multitrack enabled) you have to repeat the call for the remaining number of sectors. Obviously some of the parameters will have to be adjusted: next head, maybe next cylinder, and always sector=1. (A perfect solution would involve retrieving the disk geometry from BIOS or reading it from the BPB present on the disk). I assumed basic 1.44 MB floppy operation.

  • When reading from the disk doesn't succeed first time you should retry it a number of times. Such first time fails are perfectly normal. Five retries is a good value. In between tries you call the BIOS function that resets the diskdrive.

  • To make sure QEMU can actually read those additional 15 sectors you should pad this file so it has a total length of 16 sectors worth. The text you linked to did this also!

"Putting it all together"

[bits 16]
[org 0x7C00]

KERNEL_OFFSET equ 0x1000

xor  ax, ax
mov  ds, ax
mov  es, ax    
mov  [BOOT_DRIVE], dl
mov  ax, 0x07E0
cli
mov  ss, ax 
mov  sp, 0x1200
sti
mov  si, MSG_REAL_MODE       
call print_string           
call load_kernel            
jmp  $

print_string:
  push ax
  push bx
  push si
  mov  bx, 0x0007  ;BL=WhiteOnBlack BH=Display page 0
  mov  ah, 0x0E    ;Teletype function
 loop:
  mov  al, [si]
  cmp  al, 0
  je return
  int  0x10
  inc  si
  jmp  loop
 return:
  pop  si
  pop  bx
  pop  ax
  ret

disk_load:
  mov  [SECTORS], dh
  mov  ch, 0x00      ;C=0
  mov  dh, 0x00      ;H=0
  mov  cl, 0x02      ;S=2
 next_group:
  mov  di, 5         ;Max 5 tries
 again: 
  mov  ah, 0x02      ;Read sectors
  mov  al, [SECTORS]
  int  0x13
  jc   maybe_retry
  sub  [SECTORS], al ;Remaining sectors
  jz  ready
  mov  cl, 0x01      ;Always sector 1
  xor  dh, 1         ;Next head on diskette!
  jnz  next_group
  inc  ch            ;Next cylinder
  jmp  next_group
 maybe_retry:
  mov  ah, 0x00      ;Reset diskdrive
  int  0x13
  dec  di
  jnz  again
  jmp  disk_error
 ready:
  ret

disk_error:
  mov  si, DISK_ERROR_MSG 
  call print_string 
  jmp  $

DISK_ERROR_MSG db "Disk read error!", 0

load_kernel: 
  mov  bx, KERNEL_OFFSET       
  mov  dh, 15           
  mov  dl, [BOOT_DRIVE]                      
  call disk_load                                                  
  ret

; Global variables
BOOT_DRIVE     db 0
SECTORS        db 0
MSG_REAL_MODE  db "Started in 16-bit Real Mode", 0 

; Bootsector padding 
times 510-($-$$) db 0 
dw 0xAA55

; 15 sector padding
times 15*256 dw 0xDADA
like image 173
Sep Roland Avatar answered Oct 22 '22 14:10

Sep Roland