xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/bios.c (revision 9890706ed36aa6bfca8ad283fbe4dac12ecd692b)
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
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
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
211 get_diskinfo (int drive, struct geometry *geometry)
212 {
213   int err;
214 
215   /* Clear the flags.  */
216   geometry->flags = 0;
217 
218   if (drive & 0x80)
219     {
220       /* hard disk or CD-ROM */
221       int version;
222       unsigned long long total_sectors = 0;
223 
224       version = check_int13_extensions (drive);
225 
226       if (drive >= 0x88 || version)
227 	{
228 	  /* Possible CD-ROM - check the status.  */
229 	  if (get_cdinfo (drive, geometry))
230 	    return 0;
231 	}
232 
233       if (version)
234 	{
235 	  struct drive_parameters
236 	  {
237 	    unsigned short size;
238 	    unsigned short flags;
239 	    unsigned long cylinders;
240 	    unsigned long heads;
241 	    unsigned long sectors;
242 	    unsigned long long total_sectors;
243 	    unsigned short bytes_per_sector;
244 	    /* ver 2.0 or higher */
245 	    unsigned long EDD_configuration_parameters;
246 	    /* ver 3.0 or higher */
247 	    unsigned short signature_dpi;
248 	    unsigned char length_dpi;
249 	    unsigned char reserved[3];
250 	    unsigned char name_of_host_bus[4];
251 	    unsigned char name_of_interface_type[8];
252 	    unsigned char interface_path[8];
253 	    unsigned char device_path[8];
254 	    unsigned char reserved2;
255 	    unsigned char checksum;
256 
257 	    /* XXX: This is necessary, because the BIOS of Thinkpad X20
258 	       writes a garbage to the tail of drive parameters,
259 	       regardless of a size specified in a caller.  */
260 	    unsigned char dummy[16];
261 	  } __attribute__ ((packed)) drp;
262 
263 	  /* It is safe to clear out DRP.  */
264 	  grub_memset (&drp, 0, sizeof (drp));
265 
266 	  /* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand
267 	     the greater buffer size for the "get drive parameters" int
268 	     0x13 call in its own way.  Supposedly the BIOS assumes even
269 	     bigger space is available and thus corrupts the stack.
270 	     This is why we specify the exactly necessary size of 0x42
271 	     bytes. */
272 	  drp.size = sizeof (drp) - sizeof (drp.dummy);
273 
274 	  err = biosdisk_int13_extensions (0x4800, drive, &drp);
275 	  if (! err)
276 	    {
277 	      /* Set the LBA flag.  */
278 	      geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
279 
280 	      /* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS,
281 		 so I omit the check for now. - okuji  */
282 	      /* if (drp.flags & (1 << 1)) */
283 
284 	      if (drp.total_sectors)
285 		total_sectors = drp.total_sectors;
286 	      else
287 		/* Some buggy BIOSes doesn't return the total sectors
288 		   correctly but returns zero. So if it is zero, compute
289 		   it by C/H/S returned by the LBA BIOS call.  */
290 		total_sectors = (unsigned long long)drp.cylinders *
291 		    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 = ((unsigned long long)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 = ((unsigned long long)geometry->cylinders
336 				 * geometry->heads
337 				 * geometry->sectors);
338       geometry->sector_size = SECTOR_SIZE;
339     }
340 
341   return 0;
342 }
343