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