• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
biosdisk(int read,int drive,struct geometry * geometry,int sector,int nsec,int segment)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 /* #undef NO_INT13_FALLBACK */
86 #ifndef NO_INT13_FALLBACK
87       if (err)
88 	{
89 	  if (geometry->flags & BIOSDISK_FLAG_CDROM)
90 	    return err;
91 
92 	  geometry->flags &= ~BIOSDISK_FLAG_LBA_EXTENSION;
93 	  geometry->total_sectors = (geometry->cylinders
94 				     * geometry->heads
95 				     * geometry->sectors);
96 	  return biosdisk (read, drive, geometry, sector, nsec, segment);
97 	}
98 #endif /* ! NO_INT13_FALLBACK */
99 
100     }
101   else
102     {
103       int cylinder_offset, head_offset, sector_offset;
104       int head;
105 
106       /* SECTOR_OFFSET is counted from one, while HEAD_OFFSET and
107 	 CYLINDER_OFFSET are counted from zero.  */
108       sector_offset = sector % geometry->sectors + 1;
109       head = sector / geometry->sectors;
110       head_offset = head % geometry->heads;
111       cylinder_offset = head / geometry->heads;
112 
113       if (cylinder_offset >= geometry->cylinders)
114 	return BIOSDISK_ERROR_GEOMETRY;
115 
116       err = biosdisk_standard (read + 0x02, drive,
117 			       cylinder_offset, head_offset, sector_offset,
118 			       nsec, segment);
119     }
120 
121   return err;
122 }
123 
124 /* Check bootable CD-ROM emulation status.  */
125 static int
get_cdinfo(int drive,struct geometry * geometry)126 get_cdinfo (int drive, struct geometry *geometry)
127 {
128   int err;
129   struct iso_spec_packet
130   {
131     unsigned char size;
132     unsigned char media_type;
133     unsigned char drive_no;
134     unsigned char controller_no;
135     unsigned long image_lba;
136     unsigned short device_spec;
137     unsigned short cache_seg;
138     unsigned short load_seg;
139     unsigned short length_sec512;
140     unsigned char cylinders;
141     unsigned char sectors;
142     unsigned char heads;
143 
144     unsigned char dummy[16];
145   } __attribute__ ((packed)) cdrp;
146 
147   grub_memset (&cdrp, 0, sizeof (cdrp));
148   cdrp.size = sizeof (cdrp) - sizeof (cdrp.dummy);
149   err = biosdisk_int13_extensions (0x4B01, drive, &cdrp);
150   if (! err && cdrp.drive_no == drive)
151     {
152       if ((cdrp.media_type & 0x0F) == 0)
153         {
154           /* No emulation bootable CD-ROM */
155           geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM;
156           geometry->cylinders = 0;
157           geometry->heads = 1;
158           geometry->sectors = 15;
159           geometry->sector_size = 2048;
160           geometry->total_sectors = MAXINT;
161           return 1;
162         }
163       else
164         {
165 	  /* Floppy or hard-disk emulation */
166           geometry->cylinders
167 	    = ((unsigned int) cdrp.cylinders
168 	       + (((unsigned int) (cdrp.sectors & 0xC0)) << 2));
169           geometry->heads = cdrp.heads;
170           geometry->sectors = cdrp.sectors & 0x3F;
171           geometry->sector_size = SECTOR_SIZE;
172           geometry->total_sectors = (geometry->cylinders
173 				     * geometry->heads
174 				     * geometry->sectors);
175           return -1;
176         }
177     }
178   return 0;
179 }
180 
181 /* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return
182    non-zero, otherwise zero.  */
183 int
get_diskinfo(int drive,struct geometry * geometry)184 get_diskinfo (int drive, struct geometry *geometry)
185 {
186   int err;
187 
188   /* Clear the flags.  */
189   geometry->flags = 0;
190 
191   if (drive & 0x80)
192     {
193       /* hard disk or CD-ROM */
194       int version;
195       unsigned long total_sectors = 0;
196 
197       version = check_int13_extensions (drive);
198 
199       if (drive >= 0x88 || version)
200 	{
201 	  /* Possible CD-ROM - check the status.  */
202 	  if (get_cdinfo (drive, geometry))
203 	    return 0;
204 	}
205 
206       if (version)
207 	{
208 	  struct drive_parameters
209 	  {
210 	    unsigned short size;
211 	    unsigned short flags;
212 	    unsigned long cylinders;
213 	    unsigned long heads;
214 	    unsigned long sectors;
215 	    unsigned long long total_sectors;
216 	    unsigned short bytes_per_sector;
217 	    /* ver 2.0 or higher */
218 	    unsigned long EDD_configuration_parameters;
219 	    /* ver 3.0 or higher */
220 	    unsigned short signature_dpi;
221 	    unsigned char length_dpi;
222 	    unsigned char reserved[3];
223 	    unsigned char name_of_host_bus[4];
224 	    unsigned char name_of_interface_type[8];
225 	    unsigned char interface_path[8];
226 	    unsigned char device_path[8];
227 	    unsigned char reserved2;
228 	    unsigned char checksum;
229 
230 	    /* XXX: This is necessary, because the BIOS of Thinkpad X20
231 	       writes a garbage to the tail of drive parameters,
232 	       regardless of a size specified in a caller.  */
233 	    unsigned char dummy[16];
234 	  } __attribute__ ((packed)) drp;
235 
236 	  /* It is safe to clear out DRP.  */
237 	  grub_memset (&drp, 0, sizeof (drp));
238 
239 	  /* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand
240 	     the greater buffer size for the "get drive parameters" int
241 	     0x13 call in its own way.  Supposedly the BIOS assumes even
242 	     bigger space is available and thus corrupts the stack.
243 	     This is why we specify the exactly necessary size of 0x42
244 	     bytes. */
245 	  drp.size = sizeof (drp) - sizeof (drp.dummy);
246 
247 	  err = biosdisk_int13_extensions (0x4800, drive, &drp);
248 	  if (! err)
249 	    {
250 	      /* Set the LBA flag.  */
251 	      geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
252 
253 	      /* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS,
254 		 so I omit the check for now. - okuji  */
255 	      /* if (drp.flags & (1 << 1)) */
256 
257 	      /* FIXME: when the 2TB limit becomes critical, we must
258 		 change the type of TOTAL_SECTORS to unsigned long
259 		 long.  */
260 	      if (drp.total_sectors)
261 		total_sectors = drp.total_sectors & ~0L;
262 	      else
263 		/* Some buggy BIOSes doesn't return the total sectors
264 		   correctly but returns zero. So if it is zero, compute
265 		   it by C/H/S returned by the LBA BIOS call.  */
266 		total_sectors = drp.cylinders * drp.heads * drp.sectors;
267 	    }
268 	}
269 
270       /* Don't pass GEOMETRY directly, but pass each element instead,
271 	 so that we can change the structure easily.  */
272       err = get_diskinfo_standard (drive,
273 				   &geometry->cylinders,
274 				   &geometry->heads,
275 				   &geometry->sectors);
276       if (err)
277 	return err;
278 
279       if (! total_sectors)
280 	{
281 	  total_sectors = (geometry->cylinders
282 			   * geometry->heads
283 			   * geometry->sectors);
284 	}
285       geometry->total_sectors = total_sectors;
286       geometry->sector_size = SECTOR_SIZE;
287     }
288   else
289     {
290       /* floppy disk */
291 
292       /* First, try INT 13 AH=8h call.  */
293       err = get_diskinfo_standard (drive,
294 				   &geometry->cylinders,
295 				   &geometry->heads,
296 				   &geometry->sectors);
297 
298 #if 0
299       /* If fails, then try floppy-specific probe routine.  */
300       if (err)
301 	err = get_diskinfo_floppy (drive,
302 				   &geometry->cylinders,
303 				   &geometry->heads,
304 				   &geometry->sectors);
305 #endif
306 
307       if (err)
308 	return err;
309 
310       geometry->total_sectors = (geometry->cylinders
311 				 * geometry->heads
312 				 * geometry->sectors);
313       geometry->sector_size = SECTOR_SIZE;
314     }
315 
316   return 0;
317 }
318