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