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