From: Jonathan Walther <[EMAIL PROTECTED]>
Subject: Read Error with Asus P2B-L motherboard
Date: Tue, 18 Jul 2000 03:21:53 -0700 (PDT)

> This is wierd; I had grub 0.5.95 working from floppy, booting my
> machine; then I updated the firmware in a DOC2000 (http://m-sys.com)
> suddenly booting from the floppy would give "Read Error" right after
> stage 2.  So booting looks like this:
> 
> stage1 stage2 Read Error

  That's really odd, since, on the other hand, stage1 could
successfully load the first sector of stage2, even though the routines
in stage1.S and stage2/start.S are completely identical...

  Well, would you like to help us? First of all, it is necessary to
know what error happens in detail. So I wrote a replacement for
"stage2/start.S", which will print out the BIOS error code when the
disk read fails. Please replace "stage2/start.S" with the file
attached to this mail and recompile GRUB. Then, create a GRUB boot
floppy and see what appears on the screen. Something like this should
be displayed:

stage1 stage2 A0 <- STATUS CODE Read Error

Thanks,
Okuji
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 1996   Erich Boleyn  <[EMAIL PROTECTED]>
 *  Copyright (C) 1999   Free Software Foundation, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define ASM_FILE
#include <shared.h>

#ifndef STAGE1_5
#include <stage2_size.h>
#endif
        
/*
 *  defines for the code go here
 */

        /* Absolute addresses
           This makes the assembler generate the address without support
           from the linker. (ELF can't relocate 16-bit addresses!) */
#ifdef STAGE1_5
# define ABS(x) (x-_start+0x2000)
#else
# define ABS(x) (x-_start+0x8000)
#endif /* STAGE1_5 */
        
        /* Print message string */
#define MSG(x)  movw $ABS(x), %si; call message

        .file   "start.S"

        .text

        /* Tell GAS to generate 16-bit instructions so that this code works
           in real mode. */
        .code16

        .globl  start, _start
start:
_start: 
        /*
         * _start is loaded at 0x8000 and is jumped to with
         * CS:IP 0:0x8000 in stage2.
         */

        /* 
         * we continue to use the stack for stage1 and assume that
         * some registers are set to correct values. See stage1.S
         * for more information.
         */
        
        /* save drive reference first thing! */
        pushw   %dx

        /* print a notification message on the screen */
        pushw   %si
        MSG(notification_string)
        popw    %si

        /* this sets up for the first run through "bootloop" */
        movw    $ABS(firstlist - BOOTSEC_LISTSIZE), %di


        /* this is the loop for reading the secondary boot-loader in */
bootloop:

        /* check the number of sectors to read */
        cmpw    $0, 4(%di)

        /* if zero, go to the start function */
        je      bootit

setup_sectors:  
        /* check if we use LBA or CHS */
        cmpb    $0, -1(%si)

        /* jump to chs_mode if zero */
        je      chs_mode

lba_mode:       
        /* load logical sector start */
        movl    (%di), %ebx

        /* check for the geometry */
        cmpl    %ecx, %ebx
        jge     geometry_error

        /* the maximum is limited to 0x7f because of Phoenix EDD */
        xorl    %eax, %eax
        movb    $0x7f, %al

        /* how many do we really want to read? */
        cmpw    %ax, 4(%di)     /* compare against total number of sectors */

        /* which is greater? */
        jg      1f

        /* if less than, set to total */
        movw    4(%di), %ax

1:      
        /* subtract from total */
        subw    %ax, 4(%di)

        /* add into logical sector start */
        addl    %eax, (%di)

        /* set up disk address packet */

        /* the size and the reserved byte */
        movw    $0x0010, (%si)

        /* the number of sectors */
        movw    %ax, 2(%si)

        /* the absolute address (low 32 bits) */
        movl    %ebx, 8(%si)

        /* the segment of buffer address */
        movw    $BUFFERSEG, 6(%si)

        /* save %ax from destruction! */
        pushw   %ax

        /* zero %eax */
        xorl    %eax, %eax

        /* the offset of buffer address */
        movw    %ax, 4(%si)

        /* the absolute address (high 32 bits) */
        movl    %eax, 12(%si)


/*
 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
 *      Call with       %ah = 0x42
 *                      %dl = drive number
 *                      %ds:%si = segment:offset of disk address packet
 *      Return:
 *                      %al = 0x0 on success; err code on failure
 */

        movb    $0x42, %ah
        int     $0x13

        jc      read_error

        movw    $BUFFERSEG, %bx
        jmp     copy_buffer
                        
chs_mode:       
        /* load logical sector start (bottom half) */
        movl    (%di), %eax

        /* zero %edx */
        xorl    %edx, %edx

        /* divide by number of sectors */
        divl    (%si)

        /* save sector start */
        movb    %dl, 10(%si)

        xorl    %edx, %edx      /* zero %edx */
        divl    4(%si)          /* divide by number of heads */

        /* save head start */
        movb    %dl, 11(%si)

        /* save cylinder start */
        movw    %ax, 12(%si)

        /* do we need too many cylinders? */
        cmpw    8(%si), %ax
        jge     geometry_error

        /* determine the maximum sector length of this read */
        movw    (%si), %ax      /* get number of sectors per track/head */

        /* subtract sector start */
        subb    10(%si), %al

        /* how many do we really want to read? */
        cmpw    %ax, 4(%di)     /* compare against total number of sectors */


        /* which is greater? */
        jg      2f

        /* if less than, set to total */
        movw    4(%di), %ax

2:      
        /* subtract from total */
        subw    %ax, 4(%di)

        /* add into logical sector start */
        addl    %eax, (%di)

/*
 *  This is the loop for taking care of BIOS geometry translation (ugh!)
 */

        /* get high bits of cylinder */
        movb    13(%si), %dl

        shlb    $6, %dl         /* shift left by 6 bits */
        movb    10(%si), %cl    /* get sector */

        incb    %cl             /* normalize sector (sectors go
                                        from 1-N, not 0-(N-1) ) */
        orb     %dl, %cl        /* composite together */
        movb    12(%si), %ch    /* sector+hcyl in cl, cylinder in ch */

        /* restore %dx */
        popw    %dx
        pushw   %dx

        /* head number */
        movb    11(%si), %dh

        pushw   %ax     /* save %ax from destruction! */

/*
 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
 *      Call with       %ah = 0x2
 *                      %al = number of sectors
 *                      %ch = cylinder
 *                      %cl = sector (bits 6-7 are high bits of "cylinder")
 *                      %dh = head
 *                      %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
 *                      %es:%bx = segment:offset of buffer
 *      Return:
 *                      %al = 0x0 on success; err code on failure
 */

        movw    $BUFFERSEG, %bx
        movw    %bx, %es        /* load %es segment with disk buffer */

        xorw    %bx, %bx        /* %bx = 0, put it at 0 in the segment */
        movb    $0x2, %ah       /* function 2 */
        int     $0x13

        jc      print_status_code

        /* save source segment */
        movw    %es, %bx
        
copy_buffer:    

        /* load addresses for copy from disk buffer to destination */
        movw    6(%di), %es     /* load destination segment */

        /* restore %ax */
        popw    %ax

        /* determine the next possible destination address (presuming
                512 byte sectors!) */
        shlw    $5, %ax         /* shift %ax five bits to the left */
        addw    %ax, 6(%di)     /* add the corrected value to the destination
                                   address for next time */

        /* get the copy length */
        shlw    $4, %ax
        movw    %ax, %cx

        /* save addressing regs */
        pushw   %ds
        pushw   %si
        pushw   %di

        xorw    %di, %di        /* zero offset of destination addresses */
        xorw    %si, %si        /* zero offset of source addresses */
        movw    %bx, %ds        /* restore the source segment */

        cld             /* sets the copy direction to forward */

        /* perform copy */
        rep             /* sets a repeat */
        movsb           /* this runs the actual copy */

        /* restore addressing regs */
        popw    %di
        popw    %si
        popw    %ds

        /* check if finished with this dataset */
        cmpw    $0, 4(%di)
        jne     setup_sectors

        /* update position to load from */
        subw    $BOOTSEC_LISTSIZE, %di

        /* jump to bootloop */
        jmp     bootloop

/* END OF MAIN LOOP */

bootit:
        popw    %dx     /* this makes sure %dl is our "boot" drive */
#ifdef STAGE1_5
        ljmp    $0, $0x2200
#else /* ! STAGE1_5 */
        ljmp    $0, $0x8200
#endif /* ! STAGE1_5 */


/* Print the status code of the last operation */
print_status_code:
        /* GET STATUS OF LAST OPERATION */
        movb    $0x01, %ah
        int     $0x13

        /* Now %ah has the code */
        
        movw    $ABS(status_code_string), %si

        /* convert %ah to a readable string */  
        xorb    %al, %al
        rolw    $4, %ax
        call    digit2ascii
        movb    %al, (%si)
        
        shrw    $12, %ax
        call    digit2ascii
        movb    %al, 1(%si)

        call    message
        jmp     read_error

digit2ascii:    
        cmpb    $10, %al
        jge     1f
        addb    $0x30, %al
        jmp     2f
1:      addb    $(0x41 - 10), %al
2:      ret

status_code_string:     .string "   <- STATUS CODE "
        
        
/*
 * BIOS Geometry translation error (past the end of the disk geometry!).
 */
geometry_error:
        MSG(geometry_error_string)
        jmp     general_error

/*
 * Read error on the disk.
 */
read_error:
        MSG(read_error_string)

general_error:
        MSG(general_error_string)

/* go here when you need to stop the machine hard after an error condition */
stop:   jmp     stop

#ifdef STAGE1_5
notification_string:    .string "stage1.5 "
#else
notification_string:    .string "stage2 "
#endif
        
geometry_error_string:  .string "Geom"
read_error_string:      .string "Read"
general_error_string:   .string " Error"

/*
 * message: write the string pointed to by %si
 *
 *   WARNING: trashes %si, %ax, and %bx
 */

        /*
         * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
         *      %ah = 0xe       %al = character
         *      %bh = page      %bl = foreground color (graphics modes)
         */
1:
        movw    $0x0001, %bx
        movb    $0xe, %ah
        int     $0x10           /* display a byte */

        incw    %si
message:
        movb    (%si), %al
        cmpb    $0, %al
        jne     1b      /* if not end of string, jmp to display */
        ret
lastlist:

/*
 *  This area is an empty space between the main body of code below which
 *  grows up (fixed after compilation, but between releases it may change
 *  in size easily), and the lists of sectors to read, which grows down
 *  from a fixed top location.
 */

        .word 0
        .word 0

        . = _start + 0x200 - BOOTSEC_LISTSIZE
        
        /* fill the first data listing with the default */
blocklist_default_start:
        .long 2         /* this is the sector start parameter, in logical
                           sectors from the start of the disk, sector 0 */
blocklist_default_len:
                        /* this is the number of sectors to read */
#ifdef STAGE1_5
        .word 0         /* the command "install" will fill this up */
#else
        .word (STAGE2_SIZE + 511) >> 9
#endif
blocklist_default_seg:
#ifdef STAGE1_5
        .word 0x220
#else
        .word 0x820     /* this is the segment of the starting address
                           to load the data into */
#endif
        
firstlist:      /* this label has to be after the list data!!! */

Reply via email to