Home | History | Annotate | Download | only in stage2
      1 /* bios.c - implement C part of low-level BIOS disk input and output */
      2 /*
      3  *  GRUB  --  GRand Unified Bootloader
      4  *  Copyright (C) 1999,2000,2003,2004  Free Software Foundation, Inc.
      5  *
      6  *  This program is free software; you can redistribute it and/or modify
      7  *  it under the terms of the GNU General Public License as published by
      8  *  the Free Software Foundation; either version 2 of the License, or
      9  *  (at your option) any later version.
     10  *
     11  *  This program is distributed in the hope that it will be useful,
     12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *  GNU General Public License for more details.
     15  *
     16  *  You should have received a copy of the GNU General Public License
     17  *  along with this program; if not, write to the Free Software
     18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 #include "shared.h"
     22 
     23 
     24 /* These are defined in asm.S, and never be used elsewhere, so declare the
     26    prototypes here.  */
     27 extern int biosdisk_int13_extensions (int ax, int drive, void *dap);
     28 extern int biosdisk_standard (int ah, int drive,
     29 			      int coff, int hoff, int soff,
     30 			      int nsec, int segment);
     31 extern int check_int13_extensions (int drive);
     32 extern int get_diskinfo_standard (int drive,
     33 				  unsigned long *cylinders,
     34 				  unsigned long *heads,
     35 				  unsigned long *sectors);
     36 #if 0
     37 extern int get_diskinfo_floppy (int drive,
     38 				unsigned long *cylinders,
     39 				unsigned long *heads,
     40 				unsigned long *sectors);
     41 #endif
     42 
     43 
     44 /* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY
     46    from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it,
     47    else if READ is BIOSDISK_WRITE, then write it. If an geometry error
     48    occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then
     49    return the error number. Otherwise, return 0.  */
     50 int
     51 biosdisk (int read, int drive, struct geometry *geometry,
     52 	  int sector, int nsec, int segment)
     53 {
     54   int err;
     55 
     56   if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION)
     57     {
     58       struct disk_address_packet
     59       {
     60 	unsigned char length;
     61 	unsigned char reserved;
     62 	unsigned short blocks;
     63 	unsigned long buffer;
     64 	unsigned long long block;
     65       } __attribute__ ((packed)) dap;
     66 
     67       /* XXX: Don't check the geometry by default, because some buggy
     68 	 BIOSes don't return the number of total sectors correctly,
     69 	 even if they have working LBA support. Hell.  */
     70 #ifdef NO_BUGGY_BIOS_IN_THE_WORLD
     71       if (sector >= geometry->total_sectors)
     72 	return BIOSDISK_ERROR_GEOMETRY;
     73 #endif /* NO_BUGGY_BIOS_IN_THE_WORLD */
     74 
     75       /* FIXME: sizeof (DAP) must be 0x10. Should assert that the compiler
     76 	 can't add any padding.  */
     77       dap.length = sizeof (dap);
     78       dap.block = sector;
     79       dap.blocks = nsec;
     80       dap.reserved = 0;
     81       /* This is undocumented part. The address is formated in
     82 	 SEGMENT:ADDRESS.  */
     83       dap.buffer = segment << 16;
     84 
     85       err = biosdisk_int13_extensions ((read + 0x42) << 8, drive, &dap);
     86 
     87 /* #undef NO_INT13_FALLBACK */
     88 #ifndef NO_INT13_FALLBACK
     89       if (err)
     90 	{
     91 	  if (geometry->flags & BIOSDISK_FLAG_CDROM)
     92 	    return err;
     93 
     94 	  geometry->flags &= ~BIOSDISK_FLAG_LBA_EXTENSION;
     95 	  geometry->total_sectors = (geometry->cylinders
     96 				     * geometry->heads
     97 				     * geometry->sectors);
     98 	  return biosdisk (read, drive, geometry, sector, nsec, segment);
     99 	}
    100 #endif /* ! NO_INT13_FALLBACK */
    101 
    102     }
    103   else
    104     {
    105       int cylinder_offset, head_offset, sector_offset;
    106       int head;
    107 
    108       /* SECTOR_OFFSET is counted from one, while HEAD_OFFSET and
    109 	 CYLINDER_OFFSET are counted from zero.  */
    110       sector_offset = sector % geometry->sectors + 1;
    111       head = sector / geometry->sectors;
    112       head_offset = head % geometry->heads;
    113       cylinder_offset = head / geometry->heads;
    114 
    115       if (cylinder_offset >= geometry->cylinders)
    116 	return BIOSDISK_ERROR_GEOMETRY;
    117 
    118       err = biosdisk_standard (read + 0x02, drive,
    119 			       cylinder_offset, head_offset, sector_offset,
    120 			       nsec, segment);
    121     }
    122 
    123   return err;
    124 }
    125 
    126 /* Check bootable CD-ROM emulation status.  */
    127 static int
    128 get_cdinfo (int drive, struct geometry *geometry)
    129 {
    130   int err;
    131   struct iso_spec_packet
    132   {
    133     unsigned char size;
    134     unsigned char media_type;
    135     unsigned char drive_no;
    136     unsigned char controller_no;
    137     unsigned long image_lba;
    138     unsigned short device_spec;
    139     unsigned short cache_seg;
    140     unsigned short load_seg;
    141     unsigned short length_sec512;
    142     unsigned char cylinders;
    143     unsigned char sectors;
    144     unsigned char heads;
    145 
    146     unsigned char dummy[16];
    147   } __attribute__ ((packed)) cdrp;
    148 
    149   grub_memset (&cdrp, 0, sizeof (cdrp));
    150   cdrp.size = sizeof (cdrp) - sizeof (cdrp.dummy);
    151   err = biosdisk_int13_extensions (0x4B01, drive, &cdrp);
    152   if (! err && cdrp.drive_no == drive)
    153     {
    154       if ((cdrp.media_type & 0x0F) == 0)
    155         {
    156           /* No emulation bootable CD-ROM */
    157           geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM;
    158           geometry->cylinders = 0;
    159           geometry->heads = 1;
    160           geometry->sectors = 15;
    161           geometry->sector_size = 2048;
    162           geometry->total_sectors = MAXINT;
    163           return 1;
    164         }
    165       else
    166         {
    167 	  /* Floppy or hard-disk emulation */
    168           geometry->cylinders
    169 	    = ((unsigned int) cdrp.cylinders
    170 	       + (((unsigned int) (cdrp.sectors & 0xC0)) << 2));
    171           geometry->heads = cdrp.heads;
    172           geometry->sectors = cdrp.sectors & 0x3F;
    173           geometry->sector_size = SECTOR_SIZE;
    174           geometry->total_sectors = (geometry->cylinders
    175 				     * geometry->heads
    176 				     * geometry->sectors);
    177           return -1;
    178         }
    179     }
    180   return 0;
    181 }
    182 
    183 /* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return
    184    non-zero, otherwise zero.  */
    185 int
    186 get_diskinfo (int drive, struct geometry *geometry)
    187 {
    188   int err;
    189 
    190   /* Clear the flags.  */
    191   geometry->flags = 0;
    192 
    193   if (drive & 0x80)
    194     {
    195       /* hard disk or CD-ROM */
    196       int version;
    197       unsigned long total_sectors = 0;
    198 
    199       version = check_int13_extensions (drive);
    200 
    201       if (drive >= 0x88 || version)
    202 	{
    203 	  /* Possible CD-ROM - check the status.  */
    204 	  if (get_cdinfo (drive, geometry))
    205 	    return 0;
    206 	}
    207 
    208       if (version)
    209 	{
    210 	  struct drive_parameters
    211 	  {
    212 	    unsigned short size;
    213 	    unsigned short flags;
    214 	    unsigned long cylinders;
    215 	    unsigned long heads;
    216 	    unsigned long sectors;
    217 	    unsigned long long total_sectors;
    218 	    unsigned short bytes_per_sector;
    219 	    /* ver 2.0 or higher */
    220 	    unsigned long EDD_configuration_parameters;
    221 	    /* ver 3.0 or higher */
    222 	    unsigned short signature_dpi;
    223 	    unsigned char length_dpi;
    224 	    unsigned char reserved[3];
    225 	    unsigned char name_of_host_bus[4];
    226 	    unsigned char name_of_interface_type[8];
    227 	    unsigned char interface_path[8];
    228 	    unsigned char device_path[8];
    229 	    unsigned char reserved2;
    230 	    unsigned char checksum;
    231 
    232 	    /* XXX: This is necessary, because the BIOS of Thinkpad X20
    233 	       writes a garbage to the tail of drive parameters,
    234 	       regardless of a size specified in a caller.  */
    235 	    unsigned char dummy[16];
    236 	  } __attribute__ ((packed)) drp;
    237 
    238 	  /* It is safe to clear out DRP.  */
    239 	  grub_memset (&drp, 0, sizeof (drp));
    240 
    241 	  /* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand
    242 	     the greater buffer size for the "get drive parameters" int
    243 	     0x13 call in its own way.  Supposedly the BIOS assumes even
    244 	     bigger space is available and thus corrupts the stack.
    245 	     This is why we specify the exactly necessary size of 0x42
    246 	     bytes. */
    247 	  drp.size = sizeof (drp) - sizeof (drp.dummy);
    248 
    249 	  err = biosdisk_int13_extensions (0x4800, drive, &drp);
    250 	  if (! err)
    251 	    {
    252 	      /* Set the LBA flag.  */
    253 	      geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
    254 
    255 	      /* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS,
    256 		 so I omit the check for now. - okuji  */
    257 	      /* if (drp.flags & (1 << 1)) */
    258 
    259 	      /* FIXME: when the 2TB limit becomes critical, we must
    260 		 change the type of TOTAL_SECTORS to unsigned long
    261 		 long.  */
    262 	      if (drp.total_sectors)
    263 		total_sectors = drp.total_sectors & ~0L;
    264 	      else
    265 		/* Some buggy BIOSes doesn't return the total sectors
    266 		   correctly but returns zero. So if it is zero, compute
    267 		   it by C/H/S returned by the LBA BIOS call.  */
    268 		total_sectors = drp.cylinders * drp.heads * drp.sectors;
    269 	    }
    270 	}
    271 
    272       /* Don't pass GEOMETRY directly, but pass each element instead,
    273 	 so that we can change the structure easily.  */
    274       err = get_diskinfo_standard (drive,
    275 				   &geometry->cylinders,
    276 				   &geometry->heads,
    277 				   &geometry->sectors);
    278       if (err)
    279 	return err;
    280 
    281       if (! total_sectors)
    282 	{
    283 	  total_sectors = (geometry->cylinders
    284 			   * geometry->heads
    285 			   * geometry->sectors);
    286 	}
    287       geometry->total_sectors = total_sectors;
    288       geometry->sector_size = SECTOR_SIZE;
    289     }
    290   else
    291     {
    292       /* floppy disk */
    293 
    294       /* First, try INT 13 AH=8h call.  */
    295       err = get_diskinfo_standard (drive,
    296 				   &geometry->cylinders,
    297 				   &geometry->heads,
    298 				   &geometry->sectors);
    299 
    300 #if 0
    301       /* If fails, then try floppy-specific probe routine.  */
    302       if (err)
    303 	err = get_diskinfo_floppy (drive,
    304 				   &geometry->cylinders,
    305 				   &geometry->heads,
    306 				   &geometry->sectors);
    307 #endif
    308 
    309       if (err)
    310 	return err;
    311 
    312       geometry->total_sectors = (geometry->cylinders
    313 				 * geometry->heads
    314 				 * geometry->sectors);
    315       geometry->sector_size = SECTOR_SIZE;
    316     }
    317 
    318   return 0;
    319 }
    320